检测元素外的点击

IT技术 javascript vue.js
2021-01-14 01:18:51

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

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

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

6个回答

有一个我使用的解决方案,它基于 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 的含义

有没有办法排除外部的特定元素?例如,我在外面有一个按钮,它必须打开这个元素,因为它触发了这两种方法,所以什么也没有发生。
2021-03-14 01:18:51
工作,但在 Vue 2.0 指令不再有一个实例,所以这是未定义的。vuejs.org/v2/guide/migration.html#Custom-Directives-simplified我不知道为什么这个小提琴奏效或何时完成了这种简化。(要解决,将“this”替换为“el”,将事件绑定到元素上)
2021-03-21 01:18:51
你能解释一下 vnode.context[binding.expression](event); ?
2021-03-31 01:18:51
如何更改此设置以便可以使用表达式而不是触发 v-click-outside 中的方法?
2021-04-08 01:18:51
它可能有效,因为窗口作为“this”传递。我已经确定了答案。感谢您指出此错误。
2021-04-10 01:18:51

请注意,此解决方案仅适用于 Vue 1。

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

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/

这种方法在 Vue.js 2 中不再适用。 self.vm.$emit 调用给出了一条错误消息。
2021-03-14 01:18:51
使用@blur 也是一种选择,可以更轻松地给出相同的结果: <input @blur="hide"> where hide: function() { this.isActive = false; }
2021-03-30 01:18:51
我确实使用了 vue clickaway,但我认为您的解决方案或多或少是相同的。谢谢。
2021-04-04 01:18:51
应该编辑答案以说明我仅适用于 Vue.js 1
2021-04-06 01:18:51

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>
我们如何将其应用于滑到屏幕上的画布侧导航?除非单击,否则我无法提供 sidenav 焦点,
2021-03-18 01:18:51
这绝对是最强大的方式。谢谢!:)
2021-03-22 01:18:51
哇!我发现这是最短和最干净的解决方案。也是唯一一个在我的情况下有效的。
2021-03-23 01:18:51
出于某种原因,-1 的 tabindex 不会为我隐藏轮廓,所以我只是outline: none;为元素添加了焦点。
2021-03-24 01:18:51
只是为了添加到这一点,将 tabindex 设置为 -1 将阻止在您单击元素时出现高亮框,但它仍然允许 div 可聚焦。
2021-04-06 01:18:51

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

vue-clickaway包完美地解决了我的问题。谢谢
2021-03-22 01:18:51
@Julien Le Coupanec 我发现这个解决方案是迄今为止最好的!非常感谢分享!
2021-03-22 01:18:51
物品多怎么办?每个具有外部点击事件的项目都会在每次点击时触发事件。当你创建对话时很好,当你创建画廊时很糟糕。在非组件时代,我们正在监听来自文档的点击并检查点击了什么元素。但是现在很痛苦。
2021-04-07 01:18:51

对于 Vue 3:

此答案基于 MadisonTrash上面精彩答案,但已更新为使用新的 Vue 3 语法。

Vue 3 现在使用beforeMount代替bind,unmounted代替unbind( src )。

const clickOutside = {
  beforeMount: (el, binding) => {
    el.clickOutsideEvent = 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
        binding.value();
      }
    };
    document.addEventListener("click", el.clickOutsideEvent);
  },
  unmounted: el => {
    document.removeEventListener("click", el.clickOutsideEvent);
  },
};

createApp(App)
  .directive("click-outside", clickOutside)
  .mount("#app");
这很好用!我正在使用此指令来关闭最初未安装的模式和菜单。并且这个指令甚至在模式打开之前就触发了“关闭”事件,并且它没有出现。所以我在模态组件中添加了这段代码以使其工作:mounted: function() { setTimeout(() => { this.opened = true; }, 10); }, 卸载: function() { this.opened = false; }, 方法: { clickOutside: function() { if (this.opened) { this.$emit("close"); } },}
2021-03-28 01:18:51
谢谢。效果很好。我已经编辑了您的答案,将侦听器附加到文档而不是正文(例如,在高屏幕上的窗口中随处可见)
2021-03-29 01:18:51