$ npm install jh-outside-click --save
在日常开发中,我们常常会遇到这样的需求
点击特定元素外的时候触发某个事件
具体表现为做一个弹窗时,点击弹窗外的任意部分,如遮罩层,则自动关闭弹窗。
特定元素
:弹窗内容层
外部元素
:除了内容层外的其他一切元素
常规做法是设置遮罩层与内容层不是父子元素,然后专门在遮罩层上注册点击事件,当遮罩层被点击时,触发关闭弹窗方法。
这其实是一种曲线救国
的方法,只是实现了触发特定事件的效果,并没有监听到外部元素的点击。
如果这时候我说,没有遮罩层,那又该怎么实现呢?
方法其实很简单,不就是外部元素吗,我们直接在body上注册临时事件,判断点击元素是否为特定元素
外的,如果是,那就触发关闭方法。
怎么注册临时事件呢?
在body上注册事件是全局的,每一个元素都会被监听到,要怎么才能做到临时呢?
我们只需要在关闭方法内再去remove掉body的监听,这样就做到临时监听了。
怎么判断是否为外部元素?
方法有两种,第一种是最简单的,使用contains
node.contains( otherNode )
- node 是否包含otherNode节点.
- otherNode 是否是node的后代节点.
如果 otherNode 是 node 的后代节点或是 node 节点本身.则返回true , 否则返回 false。
第二种使用getBoundingClientRect
Element.getBoundingClientRect()方法返回元素的大小及其相对于视口的位置
通过对比当前点击元素的坐标与特定元素的位置信息,也可以得出是否为外部点击。
在原生js上实现
document.body.append(this.el); // 插入弹窗内容
// 在弹窗dom上添加自定义属性(方法)
this.el.__outsideClickEvent__ = e => {
// 判断是否外部点击
if (!this.el.contains(e.target)) {
this.hide(); // 触发关闭方法
}
};
setTimeout(() => {
// 注册事件
document.body.addEventListener('click', this.el.__outsideClickEvent__);
});
hide(){
// todo
// 移除事件
document.body.removeEventListener('click', this.el.__outsideClickEvent__);
// 删除属性
delete this.el.__outsideClickEvent__;
}
在vue上实现
在vue上我们更多是用自定义指令来实现,做法如下
directives: {
'out-side-click': {
bind(el, binding) {
el.__outsideClickEvent__ = e => {
// 也可使用getBoundingClientRect检查
if (!el.contains(e.target)) {
binding.value(e);
}
};
setTimeout(() => {
document.body.addEventListener('click', el.__outsideClickEvent__);
});
},
unbind(el) {
document.body.removeEventListener('click', el.__outsideClickEvent__);
delete el.__outsideClickEvent__;
},
},
},
<div class="content" v-out-side-click="close"></div>
题外话
如果没有了遮罩层元素,又要怎么实现非弹窗区半透明效果呢?
outline
可以帮我们解决问题,设置一个足够大的尺寸就可以了
{
rgba(0,0,0,0.8) solid 9999px
}
当然如果有做操作引导的需求,outline
也是一个好选择。
网友评论