有300ms延迟的原因
之所以移动端click会有300ms的延迟,主要是为了让用户在双击屏幕时可以对视图进行放大缩小,而这300ms就是用于判断在这时间范围之内是否有第二次点击,有则双击,否则就是单击。若每次单击屏幕都会延迟300ms才触发事件,很明显这对于用户体验来说是极其不友好的。
解决300ms延迟的办法
zepto.js
在zepto.js这个JavaScript库里,使用touch事件封装了tap事件,模仿了click的实现,并且不会有300ms的延迟。实现思路是:给目标元素添加touch监听事件,只要满足touch的时间不超过规定的超时时间(防止长按),并且touch过程不发生移动则在touchend时触发tap事件。(touchend事件是冒泡到document时才触发目标元素绑定的tap事件的)
zepto.js的穿透bug
zepto.js虽然使用tap事件模拟click避开了300ms延迟,但存在一个bug,即点击穿透。这里需要先说明的是,当用户手指触摸到屏幕的时候,系统会生成两个事件,一个是touch,另一个是click,执行顺序依次为:touchstart、touchmove、touchend、click。其中touch事件会优先处理,经过捕获、处理,、冒泡一系列流程完成后才回去触发click事件。
所谓的点击穿透,是指当用户在上层有一个模态框或覆盖层(诸如蒙版)时,当触发tap事件关闭掉模态框或覆盖层后,此时事件只进行到touchend阶段,而300ms后将会触发的click事件在上层失去了目标元素,若此时下层的位置上恰好有元素绑定了click事件或是click时会触发事件,诸如input输入框在focus时会弹出键盘,a标签会跳转链接等的元素时,则会触发其绑定的click事件。
解决穿透的办法
1:在touch事件里面,调用e.preventDefault()可以阻止本次点击触发的click事件,而且不会阻止后续的touch事件。
2: 对tap事件做一下延迟,加一个setTimeout来使上层的模态框或覆盖层等click事件触发后再消失,这样下层的元素就不会被暴露出来了,click事件自然也不会作用到下层元素上去。
3:使用css的pointer-events给下层那个会触发到click的元素添加pointer-events:none属性,这会使得该元素永远不会成为click事件的目标。需要的话可以再添加一个setTimeout再把 pointer-event 的值设回auto。
fastclick
实现原理:
监听touchstart事件获取目标元素。监听到touchend事件后,使用e.preventDefault()取消掉300ms后的click,合成相应的click事件并立即触发达到fastclick的目的。(合成事件的三步骤:创建事件,初始化鼠标事件,给目标元素分发事件)
不需要使用fastclick的场景
(上述图片来源于网络)
