检测元素外部的点击

如何检测元素外的点击?我正在使用Vue.JS,所以它将在我的模板元素之外。我知道如何在Vanilla JS中执行此操作,但我不确定在使用Vue.JS时是否有更合适的方法。

这是香草JS的解决方案:JavaScript检测DIV之外的单击事件

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

246476 次浏览

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

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 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/.

这是我使用的解决方案,它基于Linus Borg Answer,并且与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://v2.vuejs.org/v2/guide/custom-directive.html#Directive-Hook-Arguments中找到有关自定义指令和el,绑定,vnode的更多信息

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

Vue({
data: {},
methods: {
unfocused : function() {
alert('good bye');
}
}
})
<template>
<div tabindex="1" @blur="unfocused">Content inside</div>
</template>

我有一个处理切换下拉菜单的解决方案:

export default {
data() {
return {
dropdownOpen: false,
}
},
methods: {
showDropdown() {
console.log('clicked...')
this.dropdownOpen = !this.dropdownOpen
// this will control show or hide the menu
$(document).one('click.status', (e)=> {
this.dropdownOpen = false
})
},
}

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

  • https://github.com/simplesmiler/vue-clickaway.
  • https://github.com/ndelvalle/v-click-outside.

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

我用这个代码:

显示-隐藏按钮

 <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);
},
},

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

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

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');
},
});
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


}
}

只是如果有人正在寻找如何隐藏模态时,点击外面的模态。由于Modal的包装器通常包含_为ABC的类,_0或您命名的任何名称,因此您可以将@click="closeModal"放在包装器上。使用Vuejs文档中所述的事件处理,您可以检查被点击的目标是在包装器上还是在模态上。

methods: {
closeModal(e) {
this.event = function(event) {
if (event.target.className == 'modal-wrap') {
// close modal here
this.$store.commit("catalog/hideModal");
document.body.removeEventListener("click", this.event);
}
}.bind(this);
document.body.addEventListener("click", this.event);
},
}
<div class="modal-wrap" @click="closeModal">
<div class="modal">
...
</div>
<div>

我正在使用此软件包:https://www.npmjs.com/package/vue-click-outside

对我来说很好。

“ HTML:”

<div class="__card-content" v-click-outside="hide" v-if="cardContentVisible">
<div class="card-header">
<input class="subject-input" placeholder="Subject" name=""/>
</div>
<div class="card-body">
<textarea class="conversation-textarea" placeholder="Start a conversation"></textarea>
</div>
</div>

我的脚本代码:

import ClickOutside from 'vue-click-outside'
export default
{
data(){
return {
cardContentVisible:false
}
},
created()
{
},
methods:
{
openCardContent()
{
this.cardContentVisible = true;
}, hide () {
this.cardContentVisible = false
}
},
directives: {
ClickOutside
}
}

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-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>

@Denis Danilenko解决方案适合我,以下是我所做的: 顺便说一下,我在这里使用的是VueJS CLI3和NuxtJS以及Bootstrap4,但它也可以在没有NuxtJS的VueJS上工作:

<div
class="dropdown ml-auto"
:class="showDropdown ? null : 'show'">
<a
href="#"
class="nav-link"
role="button"
id="dropdownMenuLink"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
@click="showDropdown = !showDropdown"
@blur="unfocused">
<i class="fas fa-bars"></i>
</a>
<div
class="dropdown-menu dropdown-menu-right"
aria-labelledby="dropdownMenuLink"
:class="showDropdown ? null : 'show'">
<nuxt-link class="dropdown-item" to="/contact">Contact</nuxt-link>
<nuxt-link class="dropdown-item" to="/faq">FAQ</nuxt-link>
</div>
</div>
export default {
data() {
return {
showDropdown: true
}
},
methods: {
unfocused() {
this.showDropdown = !this.showDropdown;
}
}
}

您可以从指令发出自定义本地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')" )

我在主体的末尾创建了一个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;
}

如果您的组件在根元素中有多个元素,您可以将此它只是工作™解决方案与布尔值一起使用。

<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-click-outside

它简单可靠,目前被许多其他软件包使用。您还可以通过仅在所需的组件中调用包来减少JavaScript包的大小(请参阅下面的示例)。

npm install vue-click-outside

用法:

<template>
<div>
<div v-click-outside="hide" @click="toggle">Toggle</div>
<div v-show="opened">Popup item</div>
</div>
</template>


<script>
import ClickOutside from 'vue-click-outside'


export default {
data () {
return {
opened: false
}
},


methods: {
toggle () {
this.opened = true
},


hide () {
this.opened = false
}
},


mounted () {
// prevent click outside event with popupItem.
this.popupItem = this.$el
},


// do not forget this section
directives: {
ClickOutside
}
}
</script>
  <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;
}
},
},

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

我创建了一个不同的ABC_0_新包。请访问:

  • 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所取代,后者适用于任何前端框架(或普通框架)。

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

  1. 创建HTML元素,设置控件和指令
    <p @click="popup = !popup" v-out="popup">


<div v-if="popup">
My awesome popup
</div>
  1. 在数据中创建一个变量,如
data:{
popup: false,
}
  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
}
})

简短的回答是:这应该通过自定义指令来完成。

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

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

下面是至少它的定义部分的代码,请查看文章以获得完整的解释。

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

不要另起炉灶,使用此软件包V-点击-外部

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

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

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

您可以创建处理外部单击的新元件

Vue.component('click-outside', {
created: function () {
document.body.addEventListener('click', (e) => {
if (!this.$el.contains(e.target)) {
this.$emit('clickOutside');
           

})
},
template: `
<template>
<div>
<slot/>
</div>
</template>
`
})

并使用此组件:

<template>
<click-outside @clickOutside="console.log('Click outside Worked!')">
<div> Your code...</div>
</click-outside>
</template>

如果您特别希望在元素外部但仍在父元素内部进行单击,则可以使用

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

我用这个来做情态动词。

Vue 3在指令中有重大更改,所有<;VUE3方法已更改/更新。如果您想知道如何在Vue 3中执行此操作,下面是代码片段。有关信息,请参阅这个链接

<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')

对于VUE 3:

这个答案基于MadisonTrash的上面的回答很棒,但已更新为使用新的Vue 3语法。

VUE 3现在使用beforeMount代替bind,使用unmounted代替unbind船级社)。

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");

Vue 3的完整案例

这是一个基于MadisonTrash Answer的完整解决方案,BenRWB和Fredrivett对Safari兼容性和Vue 3 API更改进行了调整。

编辑:

下面提出的解决方案仍然有用,如何使用仍然有效,但我将其更改为使用document.elementsFromPoint,而不是event.contains,因为它无法将某些元素(如SVG中的<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元素是否也是子元素。如果是这种情况,它将不会触发,因为它在组件内部。

如何使用它

您只需使用AS ANY指令,并使用方法引用来处理触发器:

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

我不确定是否有人会看到这个答案,但它就在这里。 这里的想法是简单地检测是否在元素本身之外进行了任何单击。

我首先给我的";的主DIV一个ID.下拉菜单";。

<template>
<div class="relative" id="dropdown">
<div @click="openDropdown = !openDropdown" class="cursor-pointer">
<slot name="trigger" />
</div>


<div
class="absolute mt-2 w-48 origin-top-right right-0 text-red  bg-tertiary text-sm text-black"
v-show="openDropdown"
@click="openDropdown = false"
>
<slot name="content" />
</div>
</div>
</template>

然后,我只是循环通过鼠标事件的路径,并查看是否有我的ID的DIV";下拉";有吗。如果是,那么我们很好,如果不是,那么我们关闭下拉菜单。

<script>
export default {
data() {
return {
openDropdown: false,
};
},
created() {
document.addEventListener("click", (e) => {
let me = false;
for (let index = 0; index < e.path.length; index++) {
const element = e.path[index];


if (element.id == "dropdown") {
me = true;
return;
}
}


if (!me) this.openDropdown = false;
});
}
};
</script>

我敢肯定,如果您有许多嵌套元素,这可能会带来性能问题,但我发现这是最懒惰友好的方法。

现在你应该使用Vue-Click-Outside插件来实现这一点。

  1. 当你在外面点击时,

    你可以运行一个事件,那就是DIV.

    NPM插件:https://www.npmjs.com/package/v-click-outside

对于那些使用Vue 3的人。

Vue3更改了指令钩子的语法:

  • 绑定->;在安装之前
  • 取消绑定->;已卸载

Vue 3中检测元素外部的单击

点击-outside.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);
},
})
}

Register指令:

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>

这些答案中有很多似乎过于复杂,这让我怀疑我是否错过了什么。但解决方案似乎很简单:只需将this.$el(或其子项之一)与e.target进行比较。不需要非Vue事件监听器—只需要Vue@click监听器。

在本例中,我有一个呈现模态的Vue组件。如果有人单击.modal元素(它是模式后面的覆盖物),我想关闭该模式,但如果他们单击其任何子元素(即模式的内容),则不会关闭该模式。下面是执行此操作的代码:

// MyComponent.js
export default {
methods: {
handleClickModal(e) {
// Prevent clicks on child elements (e.g., `.modal-dialog`) from
// closing the modal.
if (this.$el === e.target) {
closeMyModal()
}
},
},


template: `
<div class="modal" style="display:initial" @click="handleClickModal">
<div class="modal-dialog">
<!-- modal content here... -->
</div>
</div>
`,
}

它只是工作™。

如果您正在使用vue 3script setup,则可以执行以下操作:

<script setup>
import {ref} from 'vue'


const elementRef = ref(null)
    

window.addEventListener('click', (event) => {
if (!elementRef.value.contains(event.target)){
console.log('click outside element')
}
})


</script>




<template>
<div ref="elementRef">your element</div>
</template>

全面实施(所有步骤)

尽管在这个主题下有这么多的答案,我仍然努力获得点击外部事件的完整实现。您可以按照以下步骤查看完整的实施过程。

main.JS--注册一个全局指令(只需复制并粘贴)


import Vue from 'vue';


Vue.directive('click-outside', {
bind(el, binding) {
const handler = (e) => {
if ((!el.contains(e.target) && el !== e.target)) {
binding.value(e);
}
}


el.__vueClickOutside__ = handler;
document.addEventListener('click', handler);
},
unbind(el) {
document.removeEventListener('click', el.__vueClickOutside__);
el.__vueClickOutside__ = null;
}
});

YourComponent.Vue--指令的使用。


<template>

<div>


<button @click.stop="showCalloutMenu()">Callout</button>


<section v-if="callout" v-click-outside="hideCalloutMenu">
Your callout menu content
</section>


</div>

<script>

export default {
data:() => ({
callout: false,
}),
methods: {
showCalloutMenu() {
this.callout = true;
// or if you prefer toggle effect, use
// this.callout = !this.callout;
},
hideCalloutMenu() {
this.callout = false;
},
}
};

注意事项

确保在注解元素上使用.stop事件修饰符,否则注解菜单将不会显示。

@click.stop="showCalloutMenu()"

如果有人想使用VUE 3可组合来完成此操作,您可以使用。如果您想自己编写代码,请参阅以下内容:

事件.JS

import { onMounted, onUnmounted } from 'vue';


export function useEventListener(target, event, callback) {
onMounted(() => target.addEventListener(event, callback));
onUnmounted(() => target.removeEventListener(event, callback));
}

点击-outside.JS

import { unref } from 'vue';
import { useEventListener } from './event';


export function useClickOutside(ref, cb) {
let target = null;


const listener = (event) => {
if (!target) target = unref(ref);
if (!(target === event.target || target.contains(event.target))) cb();
};


useEventListener(document.body, 'click', listener);
}

你的组件

<script setup>
import { ref } from 'vue';
import { useClickOutside } from 'path/to/click-outside';


const myCallback = () => {
console.log('You clicked outside the container!');
};


const container = ref(null);
useClickOutside(container, myCallback);
</script>


<template>
<div ref="container"></div>
</template>

注意,useEventListener是通用模式,并且可以独立于useClickOutside来使用。

除了所有伟大的答案,我只想提一下,你也可以使用Vueuse可组合OnClickOutside

下面是作为指令使用的示例

<template>
<component v-if="showDialog" v-on-click-outside="() =>showDialog = false"/>
</template>


<script>
import { vOnClickOutside } from '@vueuse/components'
const showDialog = ref(true)
</script>

也可以将其直接用作组件方法或无渲染组件。