最近H5 APP开发过程中,遇到各种许多问题,诸如点击无效,checkbox无法选中,点击穿透等问题。
一切罪责最终都指向了fastclick,于是决定要翻开其源码see see ,否则一些问题真如盲人摸象一般。
其实fastclick的代码还是蛮简单的,不多,就800多行,其中大部分代码都是在做一些Hack类的工作,主要用于判定各种类型的设备或者浏览器特性,而最终的核心代码也就是百来行吧。
其实Fastclick具体就做了以下几件事情:
1 采用touch事件替代click事件。
2 替换click默认事件延迟300毫秒为200毫秒。
3 修改正了一些IOS与android所存在兼容问题。
首先翻开代码入口,也就是构造方法:
1 | function FastClick(layer, options) { |
以上代码主要做了两件事:
1 定义了一堆的初始化变量;
2 监听了一堆的事件(根据android和ios分别监听);
当然还包括一些细节的设定,如:判定stopImmediatePropagation属性是否存在,是否存在 onclick的写法等。
1 | /** |
再往下走,就是一些基础变量的定义,主要包括设备的检测。
接下来就是一些回调方法的定义:
needsClick 方法
1 | /** |
检测元素是否需要触发原生事件。如果在元素上加上 class样式needsclick,刚不会阻止默认事件。
如checkbox或者radio,select等,点击之后是需要触发原生事件的。
比如checkbox 点击之后,需要触发复选框勾上,并且点击label的时候也会触发checkbox的勾选事件。
如果没有在label上加上needclick的样式的话,那么fastclick只会模拟一个点击事件,而之后的触发checkbox的勾选事件,将会被阻止,那么为了避免这种情况,刚需要告诉fastclick,这里需要触发原生事件。
onTouchStart 方法
1 | /** |
onTouchStart 回调方法,主要用来确定,用户第一次点击时,所点击的位置,已经所点击元素,完成初始化。
onTouchEnd 方法
1 | /** |
主要完成以下事情:
1 如果两次点击在200MS以内,则直接阻止事件.
2 判定设备,已经是否需要focus等情况;
3 最后触发事件 sendClick;
findControl 方法
这里的findControl主要是用来找到lalbel等元素绑定的control元素。
如
1 | <label for ="test"> |
通过上面的lable的for元素找到对应的 input 元素,如果是checkbox或者radio,则会触发对应的事件。
1 | /** |
尝试为label元素找到control(控制)节点。
sendClick方法(核心方法)
1 | /** |
根据用户touch的位置,合成鼠标事件,并触发。
以上基本上是fastclick的核心代码了,其实最重要的就是一个 sendClick方法,其它的都是在做一些额外的判断。
[ 后记 ]
这里我们在使用fastclick时,会出现一些问题,考虑下面的Html节点:
1 | <lable for="test"> |
正常而言,我们应该是点击label中的任何一个位置,都应该会触发checkbox的默认事件,但是在使用fastclick 时,点击span标签包裹的 “测试” 时,则不会触发checkbox的默认事件,这里主要是因为fastclick已经在模拟touch事件的时候,把默认默认事件阻止掉了,所以也就不会传播到事件到父级节点了。
但是当我们点击纯文本“测试”的事件,却可以完美的触发事件,这里主要是因为fastclick做了一个判断, 如果发现你点击的节点是文本节点,那么它把你当前点击的文本节点的父节点做为目标节点,然后并且触发focus事件,这样才能完美触发checkbox的默认事件,
注意的是,这里它只会找父节点,所以在label下的文本不能有再有嵌套的节点,如上面的span节点包含的”测试”元素的父节点只是一个span,所以无法触发focus事件。
而这个时候需要点击span包含的“测试”,并触发focus事件,那么只有两个方案:
1 给span节点添加class属性 “needsclick”,这时fastclick将不会采用touch去模拟事件,直接使用原生的事件,缺陷会是存在300MS延时。
2 修改源码
我想绝大的多数都会采用第一种方案,其实如果认真阅读一下源码,是可以修改一下源代码,根据需求去做一些判定的。