关于fastclick.js引发的惨案
in Javascript with 2 comments

关于fastclick.js引发的惨案

in Javascript with 2 comments

有一次我在制作移动端h5页面时,遇到一个很奇怪的bug,ios端在input输入框第一次可以正常触发弹起键盘,但之后却需要长按且很困难才能再次触发该行为。

抓出捣蛋的坏蛋

当时一开始我怀疑是自己的代码写了bug,但是我排查了下,我并没有去编写关于输入框聚焦事件之类的工作代码,苦思冥想,终于在搜索引擎里,看到了类似的问题,文中也提到了这种诡异情况,没错!最后的矛头指向了fastclick.js!于是我屁颠颠的跑去了github上查看issues,原来这个问题已经早有开发者提出:

确定bug发生条件

通过查看该issue,基本可以确定该bug发生的条件了:

解决的方法

仔细查看issue的回复,发现已经有人提出一种解决方法:

根据图片,在fastclick.js源码中进行修改,增加元素focus方法强制弹起,的确解决了这个bug,但是目前不知道这样修改会不会导致其他问题。

深入fastclick.js

经过查阅文档文章可以明确的了解到fastclick.js主要是解决移动端开发的两个问题:

300ms延迟

在移动设备上触发click事件,浏览器会等待300ms,以此来判断用户是否想执行双击操作,为了解决这个问题,有两种已知的解决方案:

  1. 将touch系列事件绑定在document上,通过计算touch事件触发的时间及位置来判断是否移动端触发事件,如大家熟悉的zepto.js的tap事件。
  2. 当检测到touchend事件后,通过dom自定义事件立即模拟一个click事件,并用preventDefault阻止300ms后的click事件触发,也就是fastclick.js使用的方案。

fastclick.js的具体工作流程如下:

点击穿透

打个比方有多个元素重叠在同个位置,上层元素绑定touch事件,下层绑定click事件,当上层触发touch事件将有可能触发下层的click事件,这种现象就叫点击穿透。在简书有一篇文章专门讲了如何解决点击穿透的,fastclick.js捕获顶层dom元素(html,body)的click事件,拦截所有请求然后进行判断,是否有touch触发以及阻碍click事件等。

移动页面点击穿透问题解决方案

追溯问题缘由

仔细查看大量文章后,终于缕清了一些思路,在一篇关于framework的issue的解决方案中,有位开发者提到:

我们通过这个回答和stackoverflow链接可以大概了解到发生了什么:

IOS11.3将Safari升级到了11.3,支持新的web API,允许对事件支持(passive:false)被动模式,减少屏幕滚动时的性能损耗和减少防止崩溃现象,且在IOS更新日志中提到:

Updated root document touch event listeners to use passive mode improving scrolling performance and reducing crashes.

针对document的touch事件监听添加(passive:true)被动模式的配置,且根据MDN上的说明,开启此参数后,listener将永远不会调用preventDefault()。

由于fastclick.js采用拦截click事件和监听touch事件去实现的,且对原生focus方法进行了重写,所以可能因此导致了无法重复调起聚焦事件。

另外一个bug

还发现部分开发者反映了另外一个问题,也是因为这个导致的,当静置或锁屏一段时间后,页面将不再响应任何click事件,我们可以通过渐进增强设置被动模式即可解决该bug:

参考资料

总结

终于我们又踏过一个坑,在前端多端兼容一直是很头疼的问题,IOS上也有各种奇形古怪的问题,只有踏踏实实的掉过这些坑里,才能慢慢成长,加油!成长为更好的自己!溜啦溜啦~

Responses