需求:点击当前元素之外的任何一个地方或者区域都需要隐藏当前元素,点击当前元素的任何一个地方,当前元素依然显示。如下面:点击颜色画板的任何一个区域,选择对应的功能。点击颜色画板之外的区域,隐藏颜色画板。
解决这个问题的核心关键还是利用event对象
到这个时候,我们应该打开浏览器,获取点击事件之后请一定要打开看一下!找一下里面有什么属性是可以加以利用的。(这里只讲关键的,不讲解全部的event对象里的所有属性参数方法)
target
: 当前点击元素的DOM信息(在其他事件里面就是派发事件的目标元素)
path
: 这里汇集了触发事件的DOM元素和其上层的节点的DOM信息(这会一直追溯到Window),它获取到的内容和下面方法获取到的内容是完全一样的。
document.addEventListener('mousedown', (e) => {
console.log(e)
})
let a = document.getElementsByClassName('demo')[0]
let arr = []
arr.push(a) // 先推入当前元素
getParent(a) // 执行递归
arr.push(window) // 最后再加一个window
function getParent(obj) {
if (obj.parentNode) { // 往树的上层追溯,直到最上层
arr.push(obj.parentNode)
} else {
return
}
getParent(obj.parentNode) // 递归追溯源头
}
现在,有了这俩元素之后,接下来,我们该咋用,先看代码,等会儿再讲解!
let ObjStatus = document.getElementsByClassName('status')[0]
document.addEventListener('mousedown', (e) => {
let t = document.getElementsByClassName('demo')[0] // 最外层元素
if (!e.path.includes(t)) {
ObjStatus.innerHTML = '事件触发情况:触发'
} else {
ObjStatus.innerHTML = '事件触发情况:未触发'
}
})
原理讲解
本来就只是使用e.target就行了,但是点击事件的target的机制问题,单纯地使用e.target并不能根本解决这个问题,因为目标区域内可能有很多的元素,这个点击事件会选择其内部的元素,就举当前的html代码为例子,现在我的目标区域demo内有一个p标签,如果我点到这个p标签,e.target会指向这个p标签,而不会去指向这个demo元素。又因为这个e.path含有当前元素和其所有的祖宗元素,所以可以查找e.path是否有这个demo元素,如果有,那么这个元素就是属于demo元素的内部的元素(这需要排除一些特殊情况,比如绝对布局和使用了transform的元素。),那就是说,如何点击了demo元素外的其他元素,那么当前捕获的e.path里面绝不会有demo元素。
总结
这个功能多用于关闭自己设置的弹窗,设置好最外层的判断元素,点击弹窗之外,弹窗就会关闭,这个在用户体验方面是非常有利的一个功能。
React Hooks 中的用法可参考
// 监听showChromePicker是否显示,显示之后增加下面这两个事件
const [showChromePicker, setShowChromePicker] = useState<boolean>(false);
// 监听windows点击颜色画板区域
useEffect(() => {
document.addEventListener('mousedown', handleClickOutside, true);
document.addEventListener('click', handleClickOutside, true);
return () => {
document.removeEventListener('mousedown', handleClickOutside, true);
document.removeEventListener('click', handleClickOutside, true);
};
}, [showChromePicker]);
点击之后的操作逻辑
// 递归查找父级是否在颜色画板中,不在则隐藏颜色画板
const handleClickOutside = (event: any) => {
if (showChromePicker) {
let arr: any = [];
getParent(event.target) // 执行递归
function getParent(obj: any) {
if (obj.parentNode) { // 往树的上层追溯,直到最上层
arr.push(obj.parentNode)
} else {
return
}
getParent(obj.parentNode) // 递归追溯源头
}
let chromePicker = document.getElementsByClassName('chrome-picker')[0] // 颜色画板的最外层元素类名
// 检测是否存在于arr中,不存在则说明点击的不是内部元素,否则点击的是颜色面板中的某个区域,然后做相应显示隐藏操作
if (!arr.includes(chromePicker)) {
setShowChromePicker(false);
}
}
};
交流
公众号:公众号「进军全栈攻城狮」 ,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!