我们这里所说的点击穿透,指的是在移动端中可能会出现的现象,对于移动端web开发人员来说,应该会碰到这么一个问题,那就是,假设我们屏幕上有一个遮罩,它绑定了touch事件,并且遮罩的后方存在一个绑定有click事件的元素。
现在我有一个需求,点击遮罩时,要关闭这个遮罩,让这个遮罩消失,直接 display: none
掉。
但实际上,当我点击这个遮罩时,它身后那个元素身上的click也会被触发,有时候这并不是我们想要的,那么我们应该怎么解决,又有多少种解决方案呢?
形成条件
为什么要提 形成条件,因为 点击穿透现象不是那么容易形成的,需要同时满足以下三个条件,才会出现点击穿透现象:
- 你点击的那个元素绑定了 touch 事件
- 你点击的那个元素的后方,存在着其他绑定了 click 事件的元素
- 你点击的那个元素在触发 touch 事件后,它自己或它的祖先元素立刻从页面中消失了
如果你不满足上面的全部条件,那就没办法复现。
初始代码
<style> |
方案一
阻止事件的默认行为。
btn.addEventListener("touchstart", e => { |
这是最简单最快速的方案!
适用于:你不依赖点击穿透的特性(某些人 就是故意使用点击穿透的特性来做一些 狗都做不出来的事)
方案二
给背后绑定了click事件的元素添加 pointer-events
样式
这条样式的作用是:控制元素是否可以触发事件
/* 禁止 .box 这个元素触发事件 */ |
然后,在你的touch事件函数里,对这条样式进行延迟重置
btn.addEventListener("touchstart", e => { |
说明一下,为什么我定时器里写 350ms,如果你看过我 “移动端事件” 的文章,你就会知道:
PC端事件的响应速度会被移动端慢300ms左右,而我们背后的那个元素绑定的是PC端事件click。
方案三
使背后的元素,不具备 click 特性。
我这里说的是特性,什么叫 click 特性,click 代表点击。
一个元素是否可以点击,是不是由 元素特有 或者 绑定事件 才能实现?
例如 a标签 天生就可以点击,这是它的 特性。
一个 div 天生就不能点击,但是我们给他绑定了 click 事件,也就赋予了它这个特性。
重新回来,刚刚我们说,解决方案是:使背后的元素,不具备 click 特性。
如何才能做到呢?
答案如下:
- 不给它绑定 click 事件(可以替换成别的事件)
- 把可以点击的元素替换掉,例如 a标签 替换为 div标签
<div> |
// box之前绑定的是click,现在我们给它替换成touchstart |
方案四
点击时,让元素延迟消失
btn.addEventListener("touchstart", e => { |
为什么延迟消失也能解决?
定时器的回调函数是 异步 的,异步代码不会立即执行。
所以,js引擎会先把定时器丢到事件队列内进行等待,随后它再向后找,并喊道:”还有没有click事件!!!“
没人回应js引擎,所以它就走了,它去执行事件队列中的东西去了。
所以,就不会再触发点击穿透的效果。
但是这个也有缺点,你延迟关闭遮罩,会给用户造成一种卡顿,缓慢的感觉,影响不是太好。