资讯专栏INFORMATION COLUMN

FastClick 源码解读

Chaz / 2072人阅读

摘要:所有浏览器浏览器不支持安卓中中有属性安卓中中有属性有属性的有属性的所以在不需要的浏览器会直接掉,不会执行下面的所有代码。见源码行,可以看出在响应无操作后,则触发。

其实一直就想花些时间读一读那些优秀的开源库,今天终于下了决定打算死磕下自己,2016年每个月读2-3个优秀的开源库,把源码精彩的地方和自己心得分享给大家。

目录

(一)背景
(二)源码解析
(三)Zepto 点击穿透与 FastClick
(四)新技能 Get
(五)参考文献

(一)背景

做前端的一定都知道,原生click事件在移动浏览器上会有300毫秒的延迟,会让用户觉得卡顿,这300毫秒到底是怎么来的呢,估计就不太知道了,再继续深究这300毫秒的来源是什么,应该会更少人知道吧。国外有一篇很有名的文章说的很详细,有兴趣可以看一下:what-exactly-is.....-the-300ms-click-delay。简短来说:是移动浏览器都支持双击缩放或双击滚动的操作,由于当用户第一次点击屏幕后,浏览器不能立刻判断用户确实要打开这个链接,还是想要进行双击的操作,因此几乎现在所有浏览器都效仿Safari当年的约定,在点击事件上加了300毫秒的延迟。

在这个web页面横行的年代,每个点击事件都有300毫秒的延迟是不允许的。再后来出来了很多的解决办法,比如Zepto的tap事件(会引发击穿的bug,后面会着重说),fastclick.js等都可以解决,但是多多少少会有些负作用,综合起来我最喜欢fastclick的解决方案,今天就来读一读它的源码吧~

(二)解析 1. 引入 FastClick 到自己的环境

829行:现在一般插件都会用这种方式兼容AMD、commonJs风格、原生Js的方式。还有CMD等这里没有兼容,这里可以根据自己项目需求稍作修改。

//优先兼容AMD方式
if (typeof define === "function" && typeof define.amd === "object" && define.amd) {
  define(function() {
    return FastClick;
  });
} else if (typeof module !== "undefined" && module.exports) {
  //兼容commonJs风格
  module.exports = FastClick.attach;
  module.exports.FastClick = FastClick;
} else {
  //最后兼容原生Js  
  window.FastClick = FastClick;
}    
2. 入口

824行:FastClick入口方法attach

//layer参数:要监听的dom对象,一般是document.body
//options参数:用来覆盖自定义参数,个人建议不去覆盖,
//因为里面的参数设定都是FastClick的精华,
//比如规定了touchstart和touchend事件之间的200毫秒最小间隔。
FastClick.attach = function(layer, options) {
  return new FastClick(layer, options);
};    
3. FastClick 函数 1. 23-103行:设置默认值
//比如这几个参数,上面提到不建议自定义覆盖,
//这些参数正是FastClick的精华所在,
//大幅度修改数值可能让整个库的功效大打折扣。
this.touchBoundary = options.touchBoundary || 10;
this.tapDelay = options.tapDelay || 200;
this.tapTimeout = options.tapTimeout || 700;
2. 105-107行:判断是否需要调用FastClick

官网上when-it-isnt-needed说的很清楚,以下情况不需要FastClick。

所有pc浏览器

浏览器不支持ontouchstart

安卓中chrome(all versions)meta中有user-scalable="no"属性

安卓中chrome 32+ meta中有width=device-width 属性

BlackBerry 10.3+

Firefox 27+

有-ms-touch-action: manipulation属性的IE10

有touch-action: manipulation属性的IE11

//所以在不需要FastClick的浏览器会直接return掉,
//不会执行下面的所有代码。
if (FastClick.notNeeded(layer)) {
  return;
}
3. 110-132行:自定义函数绑定在对应默认事件上
layer.addEventListener("touchstart", this.onTouchStart, false);
layer.addEventListener("touchmove", this.onTouchMove, false);
layer.addEventListener("touchend", this.onTouchEnd, false);
layer.addEventListener("touchcancel", this.onTouchCancel, false);
4. 137-159行:对旧版本android不支持 stopImmediatePropagation 事件的兼容

这里有一个知识点:stopImmediatePropagation和stopPropagation的区别,后面总结会详细说。

5. 164-173行:兼容直接绑定在dom上的onclick事件
//把 直接绑定在dom上的onclick事件
//改为绑定在该dom上的形式,
//为了之后的模拟点击事件。
if (typeof layer.onclick === "function") {
  oldOnClick = layer.onclick;
  layer.addEventListener("click", function(event) {
    oldOnClick(event);
  }, false);
  layer.onclick = null;
}
4. 兼容 & 判断

181-219行:浏览器UA判断
311-319行:determineEventType 兼容安卓chrome中的select框事件从click改为mousedown
325-355行:focus 兼容苹果手机setSelectionRange不能正确获取焦点的bug
343-367行:updateScrollParent (待看)
374-382行:getTargetElementFromEventTarget 兼容获取点击元素,iOS 4.1中会获取文字作为焦点,取它的父元素dom
497-512行:findControl

//点击label的时候,找到他对应的元素,并获取焦点

   

459-467:touchHasMoved 手指点击时移动间距大于10px,返回true
476-488:onTouchMove 手指点击时移动间距大于10px,即视为touchmove,不触发模拟click事件

5. 进阶方法

一般情况下用不到,以下方法,特殊需求可能会用到。

227-254行:needsClick 确定哪些元素需要原生的click事件
263-285行:needsFocus 确定哪些元素需要原生的focus事件

//如果哪些元素需要使用原生的click或者是focus事件,需要在dom上加上class="needsClick"
Ignored by FastClick

712-726行:destroy 这个方法只在源码中,如果有需求销毁事件,重构源码时可以调用这个方法。

6. 核心方法

391-450:onTouchStart

FastClick.prototype.onTouchStart = function(event) {
  //tapDelay默认300毫秒,点击时间差小于300毫秒,则阻止事件再次触发,阻止短时间内双击的问题
  if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
    event.preventDefault();
  }
}

521-610:onTouchEnd

if (!this.needsClick(targetElement)) {
  // 如果这不是一个需要使用原生click的元素,则屏蔽原生事件,避免触发两次click
  event.preventDefault(); 
  // 触发一次模拟的click
  this.sendClick(targetElement, event);
}

294-309:sendClick(核心方法)

//这个事件会在onTouchEnd中用到,经过一系列的判断,符合条件,调用这个模拟事件
FastClick.prototype.sendClick = function(targetElement, event) {
  var clickEvent, touch;
  //创建一个鼠标事件
  clickEvent = document.createEvent("MouseEvents");
  //初始化鼠标事件
  clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
  //触发这个事件
  targetElement.dispatchEvent(clickEvent);
};
(三)Zepto 点击穿透与 FastClick

最近项目中在用Zepto的插件touch.js中tap事件,来解决移动浏览器中300毫秒延迟的问题。但是出现了各种击穿现象

同页面tap点击弹出弹层,弹层中也有一个button,正好重叠的时候,会出现击穿

tap事件点击,页面跳转,新页面中同位置也有一个按钮,会出现击穿

我们可以看下Zepto对 singleTap 事件的处理。见源码 136-143 行,可以看出在 touchend 响应 250ms 无操作后,则触发singleTap。

//trigger single tap after 250ms of inactivity
else {
  touchTimeout = setTimeout(function(){
    touchTimeout = null
    if (touch.el) touch.el.trigger("singleTap")
    touch = {}
  }, 250)
}

用这篇文章里面的一句话来解释下Zepto的穿透问题也来说说touch事件与点击穿透问题

zepto中的 tap 通过兼听绑定在 document 上的 touch 事件来完成 tap 事件的模拟的,是通过事件冒泡实现的。在点击完成时(touchstart / touchend)的 tap 事件需要冒泡到 document 上才会触发。而在冒泡到 document 之前,手指接触和离开屏幕(touchstart / touchend)是会触发 click 事件的。

因为 click 事件有延迟(大概是300ms,为了实现safari的双击事件的设计),所以在执行完 tap 事件之后,弹出层立马就隐藏了,此时 click 事件还在延迟的 300ms 之中。当 300ms 到来的时候,click 到的其实是隐藏元素下方的元素。

如果正下方的元素有绑定 click 事件,此时便会触发,如果没有绑定 click 事件的话就当没发生。如果正下方的是 input 输入框(或是 select / radio / checkbox),点击默认 focus 而弹出输入键盘,也就出现了上面的“点透”现象。

所以到这里,个人还是建议直接使用fastclick.js库来解决移动端浏览器300毫秒的问题,不建议自己写,坑还是挺多的,这个库压缩后还是挺小的,可以用各种方式引用,来替代Zepto中的touch.js插件是个不错的办法。

(四)新技能 Get

通过读这个库,发现了很多知识上的盲区或者理解的并不是很透彻的点,再深化一下~

stopImmediatePropagation 和 stopPropagation 的区别 参考文章

他们都可以阻止事件冒泡到父元素

stopImmediatePropagation多做了一件事:阻止绑定在该元素上其他事件运行

(五)参考文献

300毫秒的起源:what-exactly-is.....-the-300ms-click-delay

stopImmediatePropagation 和 stopPropagation 的区别:http://segmentfault.com/q/1010000000120125

也来说说touch事件与点击穿透问题:http://segmentfault.com/a/1190000003848737

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/78495.html

相关文章

  • 记录fastclick中一次手动触发click事件失败

    摘要:为什么安卓可以正常工作代码在刚运行的时候,就判断是否需要使用,我的安卓测试机大于且设置了。所以在安卓下我点击使用的原生事件当然没问题。在第二次手动事件中,因为此时为,所以在中返回,接着从而顺利触发了原生事件。 在昨天的一个移动端项目中引入fastclick后手动触发click事件失败,查看了文档也没有找到解决的办法,最后通过看fastclick源码才解决。如果不想看中间这么多文字,可以...

    qieangel2013 评论0 收藏0
  • zepto touch 库源码分析

    摘要:源码分析不愿意下代码的可以直接点这里地址首先赞一下的代码注释,非常全。属性一个对象,包含了代表所有从上一次触摸事件到此次事件过程中,状态发生了改变的触点的对象。 所谓 zepto 的 touch 其实就是指这个文件啦,可以看到区区 165 行(包括注释)就完成了 swipe 和 tap 相关的事件实现。在正式开始分析源码之前,我们先说说 touch 相关的几个事件,因为无论是 tap ...

    lentrue 评论0 收藏0
  • 移动端触摸、点击事件优化(fastclick源码学习)

    摘要:移动端触摸点击事件优化源码学习最近在做一些微信移动端的页面,在此记录关于移动端触摸和点击事件的学习优化过程,主要内容围绕展开。当指针设备通常指鼠标在元素上移动时事件被触发。移动端有延迟问题,可没有哦。 移动端触摸、点击事件优化(fastclick源码学习) 最近在做一些微信移动端的页面,在此记录关于移动端触摸和点击事件的学习优化过程,主要内容围绕fastclick展开。fastclic...

    paney129 评论0 收藏0
  • 移动端触摸、点击事件优化(fastclick源码学习)

    摘要:移动端触摸点击事件优化源码学习最近在做一些微信移动端的页面,在此记录关于移动端触摸和点击事件的学习优化过程,主要内容围绕展开。当指针设备通常指鼠标在元素上移动时事件被触发。移动端有延迟问题,可没有哦。 移动端触摸、点击事件优化(fastclick源码学习) 最近在做一些微信移动端的页面,在此记录关于移动端触摸和点击事件的学习优化过程,主要内容围绕fastclick展开。fastclic...

    fxp 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<