抛出几个问题
FastClick是什么?
FastClick是一个简单、易用的库,它可以消除手机浏览器上tap和正在触发的click事件之间的延迟,这个延迟时间大约是300ms。
目的是什么呢?
目的是让提升应用的响应速度,同时,避免任何对当前逻辑的干扰。想一想如果我们每次点击屏幕的时候,都要在300ms之后才能看到响应,是多么尴尬的事情,会让人觉得卡顿。
为什么会有延迟呢?
简单来说,移动浏览器都支持双击缩放或者双击滚动的操作,由于用户第一次点击屏幕后,浏览器不能马上判断用户是要打开链接,还是想要双击,为此Safari在点击事件上加了300ms的延迟,用以判断用户点击屏幕的真正意图,后来几乎所有浏览器都效仿了这个做法。
如何避免300ms的延迟呢?
Zepto的tap事件是一个办法,但会有点透的问题,但是最新版的Zepto已经修复了这个问题。在Zepto修复问题之前,fastclick、hammer等通用库可以使用。说到Zepto之前存在的点透问题,在这里进行简单的说明。
什么是点透?
点透就是点击穿透的意思,感觉这样解释好像很简单易懂的样子,举个栗子来说明一下吧:
设定一个场景,有两个重叠的元素A和B,其中A叠在B的上方,B元素内有个Button按钮,当点击A的时候,将A隐藏,但这个时候却意外的触发了Button的点击事件,即明明点击了A,却穿透A点击到了B元素内的Button按钮,这就是所谓的点透。
为什么点透?
这就跟那300ms的延迟有关了。在移动端,当用触摸屏幕的时候不使用click而实用touch(touchstart、touchend)是因为click会有明显的延迟,造成卡顿。那么click和touch事件有什么区别呢?如下:
- touchstart:当手指触摸DOM(或者冒泡到该DOM)时,将立即触发;
- click:当手指触摸屏幕后,浏览器需要大约300ms的延迟,来判断是不是单纯的click事件。
也就是说,事件的触发会按照touchstart、touchend、click的顺序执行,也就是说touchstart阶段就已经隐藏了A元素,当click被触发的时候,能够被点击的元素是B元素内的Button按钮,因此产生了点透问题。
Zepto的tap事件
上面已经说了,为了代替click事件(也就是为了避免300ms的延迟),Zepto提出了touch.js插件中的tap事件,但tap存在点透现象:
- 同页面tap点击弹出弹层,弹层中也有一个button,正好重叠的时候,会出现击穿
- tap事件点击,页面跳转,新页面中同位置也有一个按钮,会出现击穿
通过Zepto源码,先来看看Zepto的tap点透问题是怎么产生的,源码中Zepto对 singleTap 事件的处理是这样儿的:
|
|
可以看出在touchend响应250ms无操作后,就会触发singleTap,Zepto中的tap通过兼听绑定在document上的touch事件来完成tap事件的模拟的,是通过事件冒泡实现的。在点击完成时(touchstart/touchend)的tap事件需要冒泡到document上才会触发。而在冒泡到 document 之前,手指接触和离开屏幕(touchstart/touchend)是会触发click事件的。
还是来看看fastclick是怎么办到的吧
解释完Zepto的点透问题后,我们还是回到fastclick的解决方案中来,关于其他库我在这暂且不说了,想要知道fastclick是如何搞定300ms延迟的当然要打开它的源码一探究竟了,Open it!!!!
先看到fastclick的入口:
|
|
可以看到,入口方法包含两个参数:
- layer:要监听的DOM对象,doucument.body
- options:自定义参数
在入口下面是一段兼容代码:
|
|
作为一个插件,这段代码使fastclick兼容了AMD、commonJS以及原生JS。
沿着入口,下面我们需要关注的就是fastclick的构造函数了。
首先看到一堆属性,其中需要重点关注这仨:
|
|
继续往下看,我们看到一个if语句,用来判断是否需要调用fastclick,在notNeeded方法中对多种情况进行了过滤,具体哪些情况可以对照着when-it-isnt-needed来看。
|
|
然后是将自定义方法绑定到对应的事件上
|
|
下面是对旧版本Android不支持stopImmediatePropagation 事件的兼容:
|
|
当然,还需要兼容直接绑定到DOM上的onclick事件:
|
|
接下来,又是一系列的兼容,需要说明的是:
- touchHasMoved 手指点击时移动间距大于10px,返回true
- onTouchMove 手指点击时移动间距大于10px,即视为touchmove,不触发模拟click事件
然后,是一些特殊情况的处理:
- needsClick 确定哪些元素需要原生的click事件
- needsFocus 确定哪些元素需要原生的focus事件
大概意思就是如果需要使用原生click或者focus事件,需要给DOM添加class=’needsClick’
再往下,就是fastclick的核心了!!!
- onTouchStart
- onTouchEnd
- sendClick
这里,我们抽出主要代码:
|
|
|
|
|
|