ChatGPT解决这个技术问题 Extra ChatGPT

检测点击外部元素

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

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

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

Vue 组件是隔离的。因此检测外部变化是不可能的,并且使用了反模式。
谢谢。我不确定如何在 Vue 组件中实现它。反模式还必须有一些最佳实践吗?
Vue.js 组件是隔离的,没错,但是父子通信有不同的方法。因此,与其要求检测元素外部的事件,不如指定是否要检测组件内部、父组件、某个子组件或组件之间的任何关系的元素
感谢您的反馈。你有一些我可以跟进的例子或链接吗?
github.com/simplesmiler/vue-clickaway 可以简化您的工作

t
tony19

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

Here's a small demo

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


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

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>

哇!我发现这是最短和最干净的解决方案。也是唯一对我有用的。
只是添加到这一点,将 tabindex 设置为 -1 将在您单击元素时阻止高亮框出现,但它仍然允许 div 可聚焦。
出于某种原因,-1 的 tabindex 并没有为我隐藏轮廓,所以我只是在元素的焦点上添加了 outline: none;
我们如何将其应用于滑到屏幕上的非画布侧导航?除非单击它,否则我无法赋予 sidenav 焦点,
这绝对是最强大的方式。谢谢! :)
I
Ivo Pereira

请注意,此解决方案仅适用于 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 clickaway,但我认为您的解决方案或多或少是相同的。谢谢。
这种方法在 Vue.js 2 中不再适用。 self.vm.$emit 调用会给出错误消息。
使用@blur 也是一种选择,它可以更轻松地给出相同的结果: where hide: function() { this.isActive = false; }
应编辑答案以声明它仅适用于 Vue.js 1
在 vue 1 上尝试过,即使您单击组件本身,它也会触发事件:我用它来关闭下拉菜单,即使我单击焦点,事件也会被触发并立即关闭。我知道已经有一段时间了,但我们有什么解决方案吗?
J
Julien Le Coupanec

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

https://github.com/simplesmiler/vue-clickaway

https://github.com/ndelvalle/v-click-outside


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

对于 Vue 3:

此答案基于 MadisonTrash 的 great answer above,但已更新为使用新的 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"); } },}
N
Nimantha

我使用 created() 中的函数以稍微不同的方式完成了它。

  created() {
      window.addEventListener('click', (e) => {
        if (!this.$el.contains(e.target)){
          this.showMobileNav = false
        }
      })
  },

这样,如果有人在元素之外点击,那么在我的情况下,移动导航是隐藏的。


注意:此解决方案不会取消绑定,这会以通常不明显的方式呈现内存泄漏和其他问题。选择具有解除绑定/卸载功能的解决方案,以便将来验证和稳定您的代码。
r
retrovertigo

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

与下面的@MadisonTrash 答案几乎相同
y
yann_yinn

这对我有用 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.body.removeEventListener('click', el.__vueClickOutside__)
    el.__vueClickOutside__ = null
  }
}

谢谢你的例子。在 vue 2.6 上检查了这个。有一些修复,在 unbind 方法中你必须通过这个来修复一些问题(你忘记了 unbind 方法中的 body 属性): document.body.removeEventListener('click', el.__vueClickOutside__);如果不是 - 这将导致在每次组件重新创建(页面刷新)后创建多个事件侦听器;
A
Andres Holguin

如果您专门寻找元素外的点击但仍在父元素内,您可以使用

<div class="parent" @click.self="onParentClick">
  <div class="child"></div>
</div>

我将它用于模态。


哦,谢谢,它对我有用。
N
Naren

Vue 3 对指令进行了重大更改,所有 <Vue3 方法都已更改/更新。如果您想知道如何在 Vue 3 中执行此操作,以下是代码段。有关信息,请通过 this link

<div v-click-outside="methodToInvoke"></div>

click-outside.js

export default {
  beforeMount: function (el, binding, vnode) {
    binding.event = function (event) {
      if (!(el === event.target || el.contains(event.target))) {
        if (binding.value instanceof Function) {
          binding.value(event)
        }
      }
    }
    document.body.addEventListener('click', binding.event)
  },
  unmounted: function (el, binding, vnode) {
    document.body.removeEventListener('click', binding.event)
  }
}

并在 main.js 中添加以下内容

// Directives
import ClickOutside from './click-outside'

createApp(App)
 .directive('click-outside', ClickOutside)
 .use(IfAnyModules)
 .mount('#app')

完美,谢谢🤝
b
benrwb

我已更新 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');
    },
});

x
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

  }
}

P
Pablo Huet Carrasco

vue 3的完整案例

这是一个基于 MadisonTrash 答案的完整解决方案,以及针对 safari 兼容性和 vue 3 api 更改的 benrwb 和 fredrivett 调整。

编辑:

下面提出的解决方案仍然有用,使用方法仍然有效,但我将其更改为使用 document.elementsFromPoint 而不是 event.contains,因为它不会将某些元素(如 svgs 中的 <path> 标签)识别为子元素。所以正确的指令是这个:

export default {
    beforeMount: (el, binding) => {
        el.eventSetDrag = () => {
            el.setAttribute("data-dragging", "yes");
        };
        el.eventClearDrag = () => {
            el.removeAttribute("data-dragging");
        };
        el.eventOnClick = event => {
            const dragging = el.getAttribute("data-dragging");
            // Check that the click was outside the el and its children, and wasn't a drag
            console.log(document.elementsFromPoint(event.clientX, event.clientY))
            if (!document.elementsFromPoint(event.clientX, event.clientY).includes(el) && !dragging) {
                // call method provided in attribute value
                binding.value(event);
            }
        };
        document.addEventListener("touchstart", el.eventClearDrag);
        document.addEventListener("touchmove", el.eventSetDrag);
        document.addEventListener("click", el.eventOnClick);
        document.addEventListener("touchend", el.eventOnClick);
    },
    unmounted: 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");
    },
};

老答案:

指示

const clickOutside = {
    beforeMount: (el, binding) => {
        el.eventSetDrag = () => {
            el.setAttribute("data-dragging", "yes");
        };
        el.eventClearDrag = () => {
            el.removeAttribute("data-dragging");
        };
        el.eventOnClick = event => {
            const 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
                binding.value(event);
            }
        };
        document.addEventListener("touchstart", el.eventClearDrag);
        document.addEventListener("touchmove", el.eventSetDrag);
        document.addEventListener("click", el.eventOnClick);
        document.addEventListener("touchend", el.eventOnClick);
    },
    unmounted: 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");
    },
}

createApp(App)
  .directive("click-outside", clickOutside)
  .mount("#app");

此解决方案监视应用指令的组件的元素和元素的子元素,以检查 event.target 元素是否也是子元素。如果是这种情况,它将不会触发,因为它在组件内部。

如何使用它

您只需将其用作任何指令,并使用方法引用来处理触发器:

<template>
    <div v-click-outside="myMethod">
        <div class="handle" @click="doAnotherThing($event)">
            <div>Any content</div>
        </div>
    </div>
</template>

N
Nimantha

我在正文的末尾创建了一个 div,如下所示:

<div v-if="isPopup" class="outside" v-on:click="away()"></div>

.outside 在哪里:

.outside {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0px;
  left: 0px;
}

away() 是 Vue 实例中的一个方法:

away() {
 this.isPopup = false;
}

这是一个平滑的解决方案,甚至可以使用 z-index 调整图层。
P
Pax Exterminatus

我使用这段代码:

显示隐藏按钮

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

显示隐藏元素

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

脚本

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

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


M
Martin Prestone

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

创建 html 元素,设置控件和指令

    <p @click="popup = !popup" v-out="popup">

    <div v-if="popup">
       My awesome popup
    </div>

在数据中创建一个变量,例如

data:{
   popup: false,
}

添加 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
    }
})

M
Mu-Tsun Tsai

这个问题已经有很多答案了,而且大部分都是基于类似的自定义指令思想。这种方法的问题是必须将方法函数传递给指令,并且不能像在其他事件中那样直接编写代码。

我创建了一个不同的新包 vue-on-clickout。在以下位置查看:

https://github.com/MuTsunTsai/vue-on-clickout

它允许人们像任何其他事件一样编写 v-on:clickout。例如,你可以写

<div v-on:clickout="myField=value" v-on:click="myField=otherValue">...</div>

它有效。

更新

vue-on-clickout 现在支持 Vue 3!

更新 2

vue-on-clickout 现在被一个新的包 Clickout-Event 取代,它适用于任何前端框架(或 vanilla)!


只是一个关于如何实施的问题;我需要完全加载 Javascript 文件 (<script src="clickout-event.js"></script>) 还是可以使用 import
Clickout-Event 并没有真正导出任何东西,所以我没有设计成可以作为模块导入的方式。您直接将脚本添加到您的页面,最好是在 <head> 部分。您想使用 import 是否有特殊原因?
我不需要在每个页面上,因为它在node_modules目录中,默认情况下在公共目录中不可用,所以我仍然需要手动复制文件
是的;我希望这不是太大的麻烦。
不是,但进口会很好。特别是当软件包更新时,它使生活更轻松。
s
saravanakumar

您可以像这样为单击事件注册两个事件侦听器

document.getElementById("some-area")
        .addEventListener("click", function(e){
        alert("You clicked on the area!");
        e.stopPropagation();// this will stop propagation of this event to upper level
     }
);

document.body.addEventListener("click", 
   function(e) {
           alert("You clicked outside the area!");
         }
);

谢谢。我知道这一点,但感觉在 Vue.js 中必须有更好的方法来做到这一点?
好的!让一些 vue.js 天才来回答:)
M
Martin Seydo

对于那些使用 Vue 3 的人。

Vue3 改变了 Directive Hooks 的语法:

绑定 -> beforeMount

解除绑定 -> 卸载

在 Vue 3 中检测元素外的点击:

点击外部.js

export default function directive(app) {
  // you can name the directive whatever you want. -> click-outside
  app.directive('click-outside', {
    beforeMount(el, binding) {
      el.clickOutsideEvent = (evt) => {
        evt.stopPropagation();
        if (!(el === evt.target || el.contains(evt.target))) {
          binding.value(evt, el);
        }
      };
      window.requestAnimationFrame(() => {
        document.addEventListener("click", el.clickOutsideEvent);
      });
    },
    unmounted(el) {
      document.removeEventListener("click", el.clickOutsideEvent);
    },
  })
}

注册指令:

main.js

import { createApp } from "vue";
import App from "./App.vue";

// Import your directive, in order to register it.
import clickOutside from "./directives/click-outside.js"

createApp(App).use(clickOutside).mount("#app");

用法:

<template>
  <div class="dropdown" v-click-outside="() => hideDropdown()"></div>
</template>
<script setup>
  function hideDropdown() {
    console.log("close dropdown")
  }
</script>

### OR 

<script>
  methods: {
    hideDropdown() {
      console.log("close dropdown")
    }
  }
</script> 

t
tony19

简短的回答:这应该用 Custom Directives 完成。

这里有很多很好的答案也说明了这一点,但是当您开始广泛使用外部点击(尤其是分层或具有多个排除项)时,我看到的大多数答案都会崩溃。我在媒体上写了一篇article,讨论了自定义指令的细微差别,特别是这个指令的实现。它可能无法涵盖所有边缘情况,但它涵盖了我想到的所有内容。

这将考虑多个绑定、多个级别的其他元素排除,并允许您的处理程序仅管理“业务逻辑”。

这是至少定义部分的代码,请查看文章以获取完整说明。

var handleOutsideClick={}
const OutsideClick = {
  // this directive is run on the bind and unbind hooks
  bind (el, binding, vnode) {
    // Define the function to be called on click, filter the excludes and call the handler
    handleOutsideClick[el.id] = e => {
      e.stopPropagation()
      // extract the handler and exclude from the binding value
      const { handler, exclude } = binding.value
      // set variable to keep track of if the clicked element is in the exclude list
      let clickedOnExcludedEl = false
      // if the target element has no classes, it won't be in the exclude list skip the check
      if (e.target._prevClass !== undefined) {
        // for each exclude name check if it matches any of the target element's classes
        for (const className of exclude) {
          clickedOnExcludedEl = e.target._prevClass.includes(className)
          if (clickedOnExcludedEl) {
            break // once we have found one match, stop looking
          }
        }
      }
      // don't call the handler if our directive element contains the target element
      // or if the element was in the exclude list
      if (!(el.contains(e.target) || clickedOnExcludedEl)) {
        handler()
      }
    }
    // Register our outsideClick handler on the click/touchstart listeners
    document.addEventListener('click', handleOutsideClick[el.id])
    document.addEventListener('touchstart', handleOutsideClick[el.id])
    document.onkeydown = e => {
      //this is an option but may not work right with multiple handlers
      if (e.keyCode === 27) {
        // TODO: there are minor issues when escape is clicked right after open keeping the old target
        handleOutsideClick[el.id](e)
      }
    }
  },
  unbind () {
    // If the element that has v-outside-click is removed, unbind it from listeners
    document.removeEventListener('click', handleOutsideClick[el.id])
    document.removeEventListener('touchstart', handleOutsideClick[el.id])
    document.onkeydown = null //Note that this may not work with multiple listeners
  }
}
export default OutsideClick

P
Pedro Torchio

您可以从指令发出自定义本机 javascript 事件。使用 node.dispatchEvent 创建一个从节点调度事件的指令

let handleOutsideClick;
Vue.directive('out-click', {
    bind (el, binding, vnode) {

        handleOutsideClick = (e) => {
            e.stopPropagation()
            const handler = binding.value

            if (el.contains(e.target)) {
                el.dispatchEvent(new Event('out-click')) <-- HERE
            }
        }

        document.addEventListener('click', handleOutsideClick)
        document.addEventListener('touchstart', handleOutsideClick)
    },
    unbind () {
        document.removeEventListener('click', handleOutsideClick)
        document.removeEventListener('touchstart', handleOutsideClick)
    }
})

哪个可以这样使用

h3( v-out-click @click="$emit('show')" @out-click="$emit('hide')" )

A
A1rPun

如果您有一个在根元素内包含多个元素的组件,您可以使用带有布尔值的 It just works™ 解决方案。

<template>
  <div @click="clickInside"></div>
<template>
<script>
export default {
  name: "MyComponent",
  methods: {
    clickInside() {
      this.inside = true;
      setTimeout(() => (this.inside = false), 0);
    },
    clickOutside() {
      if (this.inside) return;
      // handle outside state from here
    }
  },
  created() {
    this.__handlerRef__ = this.clickOutside.bind(this);
    document.body.addEventListener("click", this.__handlerRef__);
  },
  destroyed() {
    document.body.removeEventListener("click", this.__handlerRef__);
  },
};
</script>

这适用于 Vue 2.6,使用 Vuex 我能够实现全局下拉菜单。谢谢你。
D
Dmytro Lishtvan
  <button 
    class="dropdown"
    @click.prevent="toggle"
    ref="toggle"
    :class="{'is-active': isActiveEl}"
  >
    Click me
  </button>

  data() {
   return {
     isActiveEl: false
   }
  }, 
  created() {
    window.addEventListener('click', this.close);
  },
  beforeDestroy() {
    window.removeEventListener('click', this.close);
  },
  methods: {
    toggle: function() {
      this.isActiveEl = !this.isActiveEl;
    },
    close(e) {
      if (!this.$refs.toggle.contains(e.target)) {
        this.isActiveEl = false;
      }
    },
  },

谢谢,工作完美,如果你只需要它一次不需要额外的库
N
Nimantha

人们经常想知道用户是否离开根组件(适用于任何级别的组件)

Vue({ data: {}, methods: { unfocused : function() { alert('good bye'); } } })