// 在mounted 里 写监听
mounted() {
const handleClickOutside = (event) => {
if (!this.$el.contains(event.target)) {
this.showMoreFalg = false
}
}
document.addEventListener('click', handleClickOutside)
this.$once('hook:beforeDestroy', () => {
document.removeEventListener('click', handleClickOutside)
})
},
click 事件绑定在全局,然后通过contains这个关键性的方法,判断点击的元素是不是selectBox
的后代,要不是的话,就肯定点在了selectBox
的外面,顺道就隐藏面板。至此,最关键性的逻辑就是这个。
封装成自定义指令
最核心的逻辑就是上面了。 接下来就是封装成一个指令,专门处理这种,点击元素之外触发某种操作。
这里封装一个,clickOutside
的指令,绑定指令的元素,表示点击元素之外的地方就会执行相应的函数。
// html那边 <div v-click-outside="hidePanel" ref="selectBox" class="select-box" >
Vue.directive("click-outside", (el, bindings, vnode) => {
// el就是绑定指令的元素,bindings.expression就是动态参数这里是hide,vnode是绑定指令的元素的虚拟节点,vnode.context就是节点所在的vm实例
document.addEventListener("click", e => {
// 点击的是 绑定指令元素么 不是就执行函数
if (!el.contains(e.target)) {
let method = bindings.expression;
vnode.context[method]();
}
});
});
// 顺便记得在methods里面添加 hidePanel(){ this.showMoreFalg = false }
自定义指令的钩子
上面基本差不多了,但是还有个优化的点,因为绑定事件是在document
上,我们应该择时解绑。
在阅读完官网的文档之后,可以想到unbind
这个钩子,一旦指令解绑,document 也就会自动解绑事件。
这里注意,为了方便解绑,所以需要将点击事件赋值给el.handle
。
优化之后的指令代码如下:
Vue.directive("click-outside", {
// el就是绑定指令的元素,bindings.expression就是动态参数这里是hide,vnode是绑定指令的元素的虚拟节点,vnode.context就是节点所在的vm实例
bind(el, bindings, vnode) {
el.handle = e => {
// 点击的是 绑定指令元素么 不是就触发 参数传进来的函数
if (!el.contains(e.target)) {
let method = bindings.expression;
vnode.context[method]();
}
};
document.addEventListener("click", el.handle);
},
unbind(el) {
// 相关事件移除
document.removeEventListener("click", el.handle);
}
});