一、背景
博主所负责其中一个项目是web页面,在移动端上出现了事件穿透问题,开发介绍问题原因后,发觉是移动web一个知识点,值得记录一下。
二、click与300ms延迟
移动浏览器提供一个特殊的功能:双击(double tap)放大
300ms的延迟就来自这里,用户触碰页面之后,需要等待一段时间来判断是不是双击(double tap)动作,而不是立即响应click(单击),等待的这段时间大约是300ms。
- 移动端touch事件提供:
touchstart
、touchmove
、touchend
,却没有tap支持; - 主流框架/库都是手动实现了tap事件,以求消除300ms延迟,提高页面响应速度
- 对于简单的页面,可以把
touchstart
或者touchend
当作tap来用,但存在一些问题,比如手指接触目标元素,按住不放,慢慢移出响应区域,会触发touchstart
事件执行对应的事件处理器(本不应该触发),touchend
事件也存在类似的问题。 - 使用原生的
touch
事件存在点击穿透的问题,因为click是在touch系列事件发生后大约300ms才触发,混用touch
和click
肯定会导致事件穿透。 - 手机上响应
click
事件有300ms的延迟,会有响应慢/延迟的感觉,因此很多移动页面会使用touch/tap事件。
三、tap事件
用过Zepto
或者KISSY
等移动端js库的人肯定对tap
事件不陌生,我们做PC页面时候绑定click
,相应地手机页面就绑定tap
。但原生的touch事件本身是没有tap的,js库提供的tap事件都是模拟出来的。
Zepto
中对tap事件处理:如果在touched响应250ms无操作后,则触发singleTap。即事件触发顺序为:touch-tap-click。
三、点击穿透问题
点击穿透现象有3种:
- 点击穿透问题:点击蒙层(mask)上的关闭按钮,蒙层消失后发现触发了按钮下面元素的click事件。
- 解析:蒙层的关闭按钮绑定的是touch事件,而按钮下面元素绑定的是click事件。touch事件触发之后,蒙层消失了,300ms后这个点击的click事件fire,event的target自然就是按钮下面的元素。
- 跨页面点击穿透问题:如果按钮下面恰好是有href属性的a标签,那么页面就会发生调整。因为
a标签跳转默认是click事件触发
。原因同上。 - 第三种:这次没有mask,直接点击页面内按钮跳转至新页,然后发现新页面中对应位置元素的click事件被触发了。
- 解析:和蒙层的道理一样,js控制页面跳转的逻辑如果是绑定在touch事件上的,而且新页面中对应位置的元素绑定的是click事件,而且页面在300ms内完成了跳转,三个条件同时满足,就出现这种情况。
四、解决方案
思路:
不要混用touch和click:touch之后300ms会触发click,只有touch或者只用click就自然不会存在问题。
吃掉/消费掉touch之后的click
依旧使用tap,只是在可能发生点击穿透的情形下做额外的处理,拿个东西来挡住、或者tap后延迟350ms再隐藏mask、pointer-events、在下面元素的事件处理器里做检测。
我们对事件响应速度要求不高,最后是通过延迟隐藏/关闭mask/弹窗解决的,简单可控哈。