faskclick

    PC网页上的大部分操作都是用鼠标的,即响应的是鼠标事件,包括mousedown、mouseup、mousemove和click事件。一次点击行为,事件的触发过程为:mousedown -> mouseup -> click 三步。
手机上没有鼠标,所以就用触摸事件去实现类似的功能。touch事件包含touchstart、touchmove、touchend,注意手机上并没有tap事件。手指触发触摸事件的过程为:touchstart -> touchmove -> touchend。
    当我们手触碰屏幕时,要过300ms左右才会触发mousedown事件,所以click事件在手机上看起来就像慢半拍一样。 为什么这么设计呢? 因为它想看看你是不是要进行双击(double tap)操作。用过Zepto或KISSY等移动端js库的人肯定对tap事件不陌生,我们做PC页面时绑定click,相应地手机页面就绑定tap。但原生的touch事件本身是没有tap的,js库里提供的tap事件都是模拟出来的。
不应用 FastClick 的场景,如果 viewport meta 标签 中设置了 width=device-width, Android 上的 Chrome 32+ 会禁用 300ms 延时;
<meta name="viewport" content="width=device-width, initial-scale=1">
viewport meta 标签如果设置了 user-scalable=no,Android 上的 Chrome(所有版本)都会禁用 300ms 延迟。
 
测试延迟情况
XX.ontouchstart = function(){
        startTime =Date.now();
    };
XX.ontouchend = function(){
        pre.innerHTML += ('ontouchend : ' + (Date.now() - startTime) + '\n');
    };
XX.onclick = function(){
        pre.innerHTML += ('click : ' + (Date.now() - startTime));
    }

 

if ('addEventListener' in document) {
    document.addEventListener('DOMContentLoaded', function() {
        FastClick.attach(document.body);
    }, false);
}

//优先兼容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;
} 

核心方法

//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的插件touch.js中tap事件,来解决移动浏览器中300毫秒延迟的问题。但是出现了各种击穿现象
  1. 同页面tap点击弹出弹层,弹层中也有一个button,正好重叠的时候,会出现击穿
  2. 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)
}

  1. zepto中的 tap 通过兼听绑定在 document 上的 touch 事件来完成 tap 事件的模拟的,是通过事件冒泡实现的。在点击完成时(touchstart / touchend)的 tap 事件需要冒泡到 document 上才会触发。而在冒泡到 document 之前,手指接触和离开屏幕(touchstart / touchend)是会触发 click 事件的。
  2. 因为 click 事件有延迟(大概是300ms,为了实现safari的双击事件的设计),所以在执行完 tap 事件之后,弹出层立马就隐藏了,此时 click 事件还在延迟的 300ms 之中。当 300ms 到来的时候,click 到的其实是隐藏元素下方的元素。
  3. 如果正下方的元素有绑定 click 事件,此时便会触发,如果没有绑定 click 事件的话就当没发生。如果正下方的是 input 输入框(或是 select / radio / checkbox),点击默认 focus 而弹出输入键盘,也就出现了上面的“点透”现象。
 
 
 
posted @ 2017-09-08 11:40  圣耀  阅读(237)  评论(0编辑  收藏  举报