83 javascript vue.js
如何检测元素外的点击?我正在使用Vue.js,所以它将在我的模板元素之外.我知道如何在Vanilla JS中做到这一点,但是当我使用Vue.js时,我不确定是否有更合适的方法可以做到这一点?
这是Vanilla JS的解决方案:在div之外的Javascript Detect Click事件
我想我可以使用更好的方式来访问该元素?
Mad*_*ash 130
有我使用的解决方案,它基于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)
},
});
Run Code Online (Sandbox Code Playgroud)
有小型演示
您可以在https://vuejs.org/v2/guide/custom-directive.html#Directive-Hook-Arguments中找到有关自定义指令以及el,binding,vnode的更多信息.
Lin*_*org 68
通过设置一次自定义指令可以很好地解决:
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() }
})
Run Code Online (Sandbox Code Playgroud)
用法:
<div v-click-outside="nameOfCustomEventToCall">
Some content
</div>
Run Code Online (Sandbox Code Playgroud)
在组件中:
events: {
nameOfCustomEventToCall: function (event) {
// do something - probably hide the dropdown menu / modal etc.
}
}
Run Code Online (Sandbox Code Playgroud)
关于JSFiddle的工作演示以及有关警告的其他信息:
https://jsfiddle.net/Linusborg/yzm8t8jq/
G'o*_*r N 19
将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>
Run Code Online (Sandbox Code Playgroud)
fre*_*ett 18
此答案基于 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");
Run Code Online (Sandbox Code Playgroud)
小智 13
如果您专门寻找元素外部但仍在父级内部的点击,则可以使用
<div class="parent" @click.self="onParentClick">
<div class="child"></div>
</div>
Run Code Online (Sandbox Code Playgroud)
我用它来表示模态。
Alm*_*itt 11
我使用 created() 中的函数以稍微不同的方式做到了这一点。
created() {
window.addEventListener('click', (e) => {
if (!this.$el.contains(e.target)){
this.showMobileNav = false
}
})
},
Run Code Online (Sandbox Code Playgroud)
这样,如果有人在元素外部单击,那么在我的情况下,移动导航将被隐藏。
希望这可以帮助!
小智 9
我已经结合了所有答案(包括来自 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);
}
});
Run Code Online (Sandbox Code Playgroud)
在组件中使用:
<li v-click-outside="closeSearch">
<!-- your component here -->
</li>
Run Code Online (Sandbox Code Playgroud)
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)
}
}
Run Code Online (Sandbox Code Playgroud)
并main.js添加以下内容
// Directives
import ClickOutside from './click-outside'
createApp(App)
.directive('click-outside', ClickOutside)
.use(IfAnyModules)
.mount('#app')
Run Code Online (Sandbox Code Playgroud)
我已经更新了 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');
},
});
Run Code Online (Sandbox Code Playgroud)
小智 6
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
}
}
Run Code Online (Sandbox Code Playgroud)
小智 6
I create a div at the end of the body like that:
<div v-if="isPopup" class="outside" v-on:click="away()"></div>
Run Code Online (Sandbox Code Playgroud)
Where .outside is :
.outside {
width: 100vw;
height: 100vh;
position: fixed;
top: 0px;
left: 0px;
}
Run Code Online (Sandbox Code Playgroud)
And away() is a method in Vue instance :
away() {
this.isPopup = false;
}
Run Code Online (Sandbox Code Playgroud)
这对我有用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
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个基于 MadisonTrash 答案的完整解决方案,以及 benrwb 和 fredrivett 对 safari 兼容性和 vue 3 api 更改的调整。
下面提出的解决方案仍然有用,并且如何使用仍然有效,但我将其更改为使用document.elementsFromPoint而不是event.contains因为它无法将某些元素(例如<path>svgs 中的标签)识别为子元素。所以正确的指令是这个:
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");
},
};
Run Code Online (Sandbox Code Playgroud)
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");
},
};
Run Code Online (Sandbox Code Playgroud)
此解决方案监视应用指令的组件的元素和元素的子元素,以检查event.target元素是否也是子元素。如果是这种情况,它将不会触发,因为它在组件内部。
您只需使用任何指令,并使用方法引用来处理触发器:
<template>
<div v-click-outside="myMethod">
<div class="handle" @click="doAnotherThing($event)">
<div>Any content</div>
</div>
</div>
</template>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
62463 次 |
| 最近记录: |