Javascript 检测元素外的点击

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36170425/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 18:43:07  来源:igfitidea点击:

Detect click outside element

javascriptvue.js

提问by MadisonTrash

How can I detect a click outside my element? I'm using Vue.js so it's gonna be outside my templates element. I know how to do it in Vanilla JS, but I'm not sure if there's a more proper way to do it, when I'm using Vue.js?

如何检测元素外的点击?我正在使用 Vue.js 所以它会在我的模板元素之外。我知道如何在 Vanilla JS 中做到这一点,但是当我使用 Vue.js 时,我不确定是否有更合适的方法来做到这一点?

This is the solution for Vanilla JS: Javascript Detect Click event outside of div

这是 Vanilla JS 的解决方案:Javascript Detect Click event outside of div

I guess I can use a better way to access the element?

我想我可以使用更好的方法来访问元素?

采纳答案by Linus Borg

Can be solved nicely by setting up a custom directive once:

可以通过设置一次自定义指令很好地解决:

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() }
})

Usage:

用法:

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

In the component:

在组件中:

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

Working Demo on JSFiddle with additional info about caveats:

JSFiddle 上的工作演示以及有关警告的附加信息:

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

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

回答by MadisonTrash

There is the solution I used, which is based on Linus Borg answer and works fine with vue.js 2.0.

有一个我使用的解决方案,它基于 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 childrens
      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)
  },
});

You bind to it using v-click-outside:

你绑定到它使用v-click-outside

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

Here's a small demo

这是一个小演示

You can find some more info about custom directives and what el, binding, vnodemeans in https://vuejs.org/v2/guide/custom-directive.html#Directive-Hook-Arguments

您可以在https://vuejs.org/v2/guide/custom-directive.html#Directive-Hook-Arguments 中找到有关自定义指令以及el、binding、vnode含义的更多信息

回答by G'ofur N

Add tabindexattribute to your component so that it can be focused and do the following:

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>

回答by Julien Le Coupanec

There are two packages available in the community for this task (both are maintained):

社区中有两个包可用于此任务(均已维护):

回答by xiaoyu2er

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

  }
}

回答by yann_yinn

This Worked for me with Vue.js 2.5.2 :

这对我有用 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
  }
}

回答by BogdanG

I have combined all answers (including a line from vue-clickaway) and came up with this solution that works for me:

我已经结合了所有答案(包括来自 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);
    }
});

Use in component:

在组件中使用:

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

回答by Pax Exterminatus

I use this code:

我使用这个代码:

show-hide button

显示隐藏按钮

 <a @click.stop="visualSwitch()"> show hide </a>

show-hide element

显示隐藏元素

<div class="dialog-popup" v-if="visualState" @click.stop=""></div>

script

脚本

data () { return {
    visualState: false,
}},
methods: {
    visualSwitch() {
        this.visualState = !this.visualState;
        if (this.visualState)
            document.addEventListener('click', this.visualState);
        else
            document.removeEventListener('click', this.visualState);
    },
},

Update:remove watch; add stop propagation

更新:移除手表;添加停止传播

回答by benrwb

I have updated MadisonTrash's answer to support Mobile Safari (which does not have clickevent, touchendmust be used instead). This also incorporates a check so that the event isn't triggered by dragging on mobile devices.

我已经更新了 MadisonTrash 的答案以支持 Mobile Safari(它没有click事件,touchend必须改用)。这还包含一个检查,以便在移动设备上拖动不会触发事件。

Vue.directive('click-outside', {
    bind: function (el, binding, vnode) {
        el.eventSetDrag = function () {
            el.setAttribute('data-dragging', 'yes');
        }
        el.eventClearDrag = function () {
            el.removeAttribute('data-dragging');
        }
        el.eventOnClick = function (event) {
            var dragging = el.getAttribute('data-dragging');
            // Check that the click was outside the el and its children, and wasn't a drag
            if (!(el == event.target || el.contains(event.target)) && !dragging) {
                // call method provided in attribute value
                vnode.context[binding.expression](event);
            }
        };
        document.addEventListener('touchstart', el.eventClearDrag);
        document.addEventListener('touchmove', el.eventSetDrag);
        document.addEventListener('click', el.eventOnClick);
        document.addEventListener('touchend', el.eventOnClick);
    }, unbind: function (el) {
        document.removeEventListener('touchstart', el.eventClearDrag);
        document.removeEventListener('touchmove', el.eventSetDrag);
        document.removeEventListener('click', el.eventOnClick);
        document.removeEventListener('touchend', el.eventOnClick);
        el.removeAttribute('data-dragging');
    },
});

回答by Martin Prestone

I hate additional functions so... here is an awesome vue solution without an additional vue methods, only var

我讨厌附加功能所以......这是一个很棒的 vue 解决方案,没有额外的 vue 方法,只有 var

  1. create html element, set controls and directive
  1. 创建 html 元素,设置控件和指令
    <p @click="popup = !popup" v-out="popup">

    <div v-if="popup">
       My awesome popup
    </div>
  1. create a var in data like
  1. 在数据中创建一个变量,如
data:{
   popup: false,
}
  1. add vue directive. its
  1. 添加 vue 指令。它的
Vue.directive('out', {

    bind: function (el, binding, vNode) {
        const handler = (e) => {
            if (!el.contains(e.target) && el !== e.target) {
                //and here is you toggle var. thats it
                vNode.context[binding.expression] = false
            }
        }
        el.out = handler
        document.addEventListener('click', handler)
    },

    unbind: function (el, binding) {
        document.removeEventListener('click', el.out)
        el.out = null
    }
})