移动端点击穿透问题由来
这要追溯至 2007 年初。苹果公司在发布首款 iPhone 前夕,遇到一个问题:当时的网站都是为大屏幕设备所设计的。于是苹果的工程师们做了一些约定,应对 iPhone 这种小屏幕浏览桌面端站点的问题。这当中最出名的,当属双击缩放(double tap to zoom),这也是会有上述 300 毫秒延迟的主要原因。双击缩放,顾名思义,即用手指在屏幕上快速点击两次,iOS 自带的 Safari 浏览器会将网页缩放至原始比例。 那么这和 300 毫秒延迟有什么联系呢? 假定这么一个场景。用户在 iOS Safari 里边点击了一个链接。由于用户可以进行双击缩放或者双击滚动的操作,当用户一次点击屏幕之后,浏览器并不能立刻判断用户是确实要打开这个链接,还是想要进行双击操作。因此,iOS Safari 就等待 300 毫秒,以判断用户是否再次点击了屏幕。 鉴于iPhone的成功,其他移动浏览器都复制了 iPhone Safari 浏览器的多数约定,包括双击缩放,几乎现在所有的移动端浏览器都有这个功能。之前人们刚刚接触移动端的页面,在欣喜的时候往往不会care这个300ms的延时问题,可是如今touch端界面如雨后春笋,用户对体验的要求也更高,这300ms带来的卡顿慢慢变得让人难以接受。
那神马是移动端穿透呢?假如页面上有两个元素A和B。B元素在A元素之上。我们在B元素的touchstart事件上注册了一个回调函数,该回调函数的作用是隐藏B元素。我们发现,当我们点击B元素,B元素被隐藏了,B在300ms后产生的click事件由于找不到B,B会把click事件强行加给A执行,这个过程就叫点击穿透,动画如下

分析A产生click事件的原因,由于点击B后B会依次发生touchstart--->touchend-->click事件,但当B的touchstart事件产生后,B消失了,B会把它的300ms后产生的click事件强加到它z轴下方的A上,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<style>
#A{
font-size:50px;
width:400px;
height:400px;
background:gray
}
#B{
font-size:50px;
width:400px;
height:400px;
background:blue;
position:absolute;
left: 200px;
top: 200px;
}
</style>
<div id="A">A</div>
<div id="B">B:点我穿透A</div>
<script>
let A=document.getElementById('A')
let B=document.getElementById('B')
B.addEventListener('touchstart',function(){
B.style.display="none"
})
A.addEventListener('click',function(){
console.log('A:啊!我被穿透了')
})
</script>
</body>
</html>
大家请注意我把meta标签注释了,因为当我们在 meta标签加上 width=device-width 和 user-scalable=0时,移动端的浏览器就会自动去掉click的事件300ms的延时,因为我们已经为用户适配了页面大小和阻止了用户缩放,所有浏览器就不用判断用户双击缩放了,因此浏览器就自动取消了click事件300ms的延迟
那么如何消除点击穿透呢,大家先来看这张图。

小编第一次学习解决移动端点击穿透的时候,觉得这之间非常绕,往往搞不清事件的根源,后来用了简单的数学分析,才觉得思路特别清晰,
首先我们先来看产生点击穿透的必要条件:
1.meta标签没有width=device-width和 user-scalable=0
2.B(mask)蒙版在A之上,且在点击后B在300ms之前消失
3.B(mask)绑定的是touchstart事件,A绑定的是click事件
所以解决的方案无非就是不让上述三种情况不发生而已,是不是瞬间脑子就灵光了。
解决方案:
1.meta 标签设置 width=device-width 和 user-scalable=0
2.在整个项目中只用touchstart事件,缺点:a标签的herf跳转仍需要click事件。
3.在整个项目中只用click事件,优点:使用简单,适合使用对用户点击事件响应实时性不高的 应 用, 缺点:点击仍有300ms的延迟,不适合游戏类的应用
4.在点击B后,不让B立刻消失,给B添加一个渐进(fade)消失的动画效果,时间大于300ms 就可以了
5.最后一个是我觉得是最好的解决方案,消除300ms的延迟后,再使用click事件,使用fastclick (大小 10kB)这个库来实现,只需要多加几kB的大小,还是很划算的
总结:解决点击穿透的方式还是比较多的,具体使用那种方案还是结合实际项目才能决定