[javascript] 요소 외부 클릭 감지

요소 외부에서 클릭을 감지하려면 어떻게해야합니까? Vue.js를 사용하고 있으므로 템플릿 요소 외부에 있습니다. Vanilla JS에서 수행하는 방법을 알고 있지만 Vue.js를 사용할 때 더 적절한 방법이 있는지 잘 모르겠습니다.

이것은 Vanilla JS의 솔루션 입니다. div 외부의 Javascript Detect Click 이벤트

요소에 액세스하는 더 나은 방법을 사용할 수 있습니까?



답변

사용자 지정 지시문을 한 번 설정하면 멋지게 해결할 수 있습니다.

Vue.directive('click-outside', {
  bind () {
      this.event = event => this.vm.$emit(this.expression, event)
      this.el.addEventListener('click', this.stopProp)
      document.body.addEventListener('click', this.event)
  },
  unbind() {
    this.el.removeEventListener('click', this.stopProp)
    document.body.removeEventListener('click', this.event)
  },

  stopProp(event) { event.stopPropagation() }
})

용법:

<div v-click-outside="nameOfCustomEventToCall">
  Some content
</div>

구성 요소에서 :

events: {
  nameOfCustomEventToCall: function (event) {
    // do something - probably hide the dropdown menu / modal etc.
  }
}

경고에 대한 추가 정보가 포함 된 JSFiddle의 작업 데모 :

https://jsfiddle.net/Linusborg/yzm8t8jq/


답변

Linus Borg 답변을 기반으로하고 vue.js 2.0에서 잘 작동하는 내가 사용한 솔루션이 있습니다.

Vue.directive('click-outside', {
  bind: function (el, binding, vnode) {
    el.clickOutsideEvent = function (event) {
      // here I check that click was outside the el and his children
      if (!(el == event.target || el.contains(event.target))) {
        // and if it did, call method provided in attribute value
        vnode.context[binding.expression](event);
      }
    };
    document.body.addEventListener('click', el.clickOutsideEvent)
  },
  unbind: function (el) {
    document.body.removeEventListener('click', el.clickOutsideEvent)
  },
});

다음을 사용하여 바인딩합니다 v-click-outside.

<div v-click-outside="doStuff">

다음은 작은 데모입니다.

https://vuejs.org/v2/guide/custom-directive.html#Directive-Hook-Arguments 에서 사용자 지정 지시문과 el, binding, vnode의 의미에 대한 자세한 정보를 찾을 수 있습니다.


답변

tabindex집중할 수 있도록 구성 요소에 속성을 추가 하고 다음을 수행합니다.

<template>
    <div
        @focus="handleFocus"
        @focusout="handleFocusOut"
        tabindex="0"
    >
      SOME CONTENT HERE
    </div>
</template>

<script>
export default {
    methods: {
        handleFocus() {
            // do something here
        },
        handleFocusOut() {
            // do something here
        }
    }
}
</script>


답변

이 작업을 위해 커뮤니티에서 두 가지 패키지를 사용할 수 있습니다 (둘 다 유지 관리 됨).


답변

이것은 Vue.js 2.5.2에서 나를 위해 일했습니다.

/**
 * Call a function when a click is detected outside of the
 * current DOM node ( AND its children )
 *
 * Example :
 *
 * <template>
 *   <div v-click-outside="onClickOutside">Hello</div>
 * </template>
 *
 * <script>
 * import clickOutside from '../../../../directives/clickOutside'
 * export default {
 *   directives: {
 *     clickOutside
 *   },
 *   data () {
 *     return {
         showDatePicker: false
 *     }
 *   },
 *   methods: {
 *     onClickOutside (event) {
 *       this.showDatePicker = false
 *     }
 *   }
 * }
 * </script>
 */
export default {
  bind: function (el, binding, vNode) {
    el.__vueClickOutside__ = event => {
      if (!el.contains(event.target)) {
        // call method provided in v-click-outside value
        vNode.context[binding.expression](event)
        event.stopPropagation()
      }
    }
    document.body.addEventListener('click', el.__vueClickOutside__)
  },
  unbind: function (el, binding, vNode) {
    // Remove Event Listeners
    document.removeEventListener('click', el.__vueClickOutside__)
    el.__vueClickOutside__ = null
  }
}


답변

export default {
  bind: function (el, binding, vNode) {
    // Provided expression must evaluate to a function.
    if (typeof binding.value !== 'function') {
      const compName = vNode.context.name
      let warn = `[Vue-click-outside:] provided expression '${binding.expression}' is not a function, but has to be`
      if (compName) { warn += `Found in component '${compName}'` }

      console.warn(warn)
    }
    // Define Handler and cache it on the element
    const bubble = binding.modifiers.bubble
    const handler = (e) => {
      if (bubble || (!el.contains(e.target) && el !== e.target)) {
        binding.value(e)
      }
    }
    el.__vueClickOutside__ = handler

    // add Event Listeners
    document.addEventListener('click', handler)
  },

  unbind: function (el, binding) {
    // Remove Event Listeners
    document.removeEventListener('click', el.__vueClickOutside__)
    el.__vueClickOutside__ = null

  }
}


답변

나는 모든 답변 (vue-clickaway의 라인 포함)을 결합하고 나를 위해 작동하는이 솔루션을 생각해 냈습니다.

Vue.directive('click-outside', {
    bind(el, binding, vnode) {
        var vm = vnode.context;
        var callback = binding.value;

        el.clickOutsideEvent = function (event) {
            if (!(el == event.target || el.contains(event.target))) {
                return callback.call(vm, event);
            }
        };
        document.body.addEventListener('click', el.clickOutsideEvent);
    },
    unbind(el) {
        document.body.removeEventListener('click', el.clickOutsideEvent);
    }
});

구성 요소에서 사용 :

<li v-click-outside="closeSearch">
  <!-- your component here -->
</li>