DDR爱好者之家 Design By 杰米

近期使用tap事件为老夫带来了这样那样的问题,其中一个问题是解决了点透还需要将原来一个个click变为tap,这样的话我们就抛弃了ie用户

当然可以做兼容,但是没人想动老代码的,于是今天拿出了fastclick这个东西,

这是最近第四次发文说tap的点透事件,我们一直对解决“点透”的蒙版耿耿于怀,于是今天老大提出了一个库fastclick,最后证明解决了我们的问题

而且click不必替换为tap了,于是我们老大就语重心长的对我说了一句,你们就误我吧,我邮件都发出去了......

于是我下午就在看fastclick这个库,看看是不是能解决我们的问题,于是我们开始吧

读fastclick源码

尼玛使用太简单了,直接一句:

FastClick.attach(document.body);

于是所有的click响应速度直接提升,刚刚的!什么input获取焦点的问题也解决了!!!尼玛如果真的可以的话,原来改页面的同事肯定会啃了我

一步步来,我们跟进去,入口就是attach方法:

FastClick.attach = function(layer) {
'use strict';
return new FastClick(layer);
};

这个兄弟不过实例化了下代码,所以我们还要看我们的构造函数:

function FastClick(layer) {
'use strict';
var oldOnClick, self = this;
  this.trackingClick = false;
  this.trackingClickStart = 0;
  this.targetElement = null;
  this.touchStartX = 0;
  this.touchStartY = 0;
  this.lastTouchIdentifier = 0;
  this.touchBoundary = 10;
  this.layer = layer;
  if (!layer || !layer.nodeType) {
   throw new TypeError('Layer must be a document node');
  }
  this.onClick = function() { return FastClick.prototype.onClick.apply(self, arguments); };
  this.onMouse = function() { return FastClick.prototype.onMouse.apply(self, arguments); };
  this.onTouchStart = function() { return FastClick.prototype.onTouchStart.apply(self, arguments); };
  this.onTouchMove = function() { return FastClick.prototype.onTouchMove.apply(self, arguments); };
  this.onTouchEnd = function() { return FastClick.prototype.onTouchEnd.apply(self, arguments); };
  this.onTouchCancel = function() { return FastClick.prototype.onTouchCancel.apply(self, arguments); };
  if (FastClick.notNeeded(layer)) {
   return;
  }
  if (this.deviceIsAndroid) {
   layer.addEventListener('mouseover', this.onMouse, true);
   layer.addEventListener('mousedown', this.onMouse, true);
   layer.addEventListener('mouseup', this.onMouse, true);
  }
  layer.addEventListener('click', this.onClick, true);
  layer.addEventListener('touchstart', this.onTouchStart, false);
  layer.addEventListener('touchmove', this.onTouchMove, false);
  layer.addEventListener('touchend', this.onTouchEnd, false);
  layer.addEventListener('touchcancel', this.onTouchCancel, false);
 
  if (!Event.prototype.stopImmediatePropagation) {
   layer.removeEventListener = function(type, callback, capture) {
    var rmv = Node.prototype.removeEventListener;
    if (type === 'click') {
     rmv.call(layer, type, callback.hijacked || callback, capture);
    } else {
     rmv.call(layer, type, callback, capture);
    }
   };
 
   layer.addEventListener = function(type, callback, capture) {
    var adv = Node.prototype.addEventListener;
    if (type === 'click') {
     adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
      if (!event.propagationStopped) {
       callback(event);
      }
     }), capture);
    } else {
     adv.call(layer, type, callback, capture);
    }
   };
  }
  if (typeof layer.onclick === 'function') {
   oldOnClick = layer.onclick;
   layer.addEventListener('click', function(event) {
 oldOnClick(event);
 }, false);
 layer.onclick = null;
}
}

看看这段代码,上面很多属性干了什么事情我也不知道......于是忽略了

if (!layer || !layer.nodeType) {
throw new TypeError('Layer must be a document node');
}

其中这里要注意,我们必须传入一个节点给构造函数,否则会出问题

然后这个家伙将一些基本的鼠标事件注册在自己的属性方法上了,具体是干神马的我们后面再说

在后面点有个notNeeded方法:

 FastClick.notNeeded = function(layer) {
  'use strict';
  var metaViewport;
  if (typeof window.ontouchstart === 'undefined') {
   return true;
  }
  if ((/Chrome\/[0-9]+/).test(navigator.userAgent)) {
   if (FastClick.prototype.deviceIsAndroid) {
    metaViewport = document.querySelector('meta[name=viewport]');
    if (metaViewport && metaViewport.content.indexOf('user-scalable=no') !== -1) {
     return true;
    }
   } else {
    return true;
   }
  }
  if (layer.style.msTouchAction === 'none') {
   return true;
  }
  return false;
 };

这个方法用于判断是否需要用到fastclick,注释的意思不太明白,我们看看代码吧

首先一句:

if (typeof window.ontouchstart === 'undefined') {
 return true;
}

如果不支持touchstart事件的话,返回true
PS:现在的只管感受就是fastclick应该也是以touch事件模拟的,但是其没有点透问题

后面还判断了android的一些问题,我这里就不关注了,意思应该就是支持touch才能支持吧,于是回到主干代码

主干代码中,我们看到,如果浏览器不支持touch事件或者其它问题就直接跳出了

然后里面有个deviceIsAndroid的属性,我们跟去看看(其实不用看也知道是判断是否是android设备)

FastClick.prototype.deviceIsAndroid = navigator.userAgent.indexOf('Android') > 0;

绑定事件

好了,这家伙开始绑定注册事件了,至此还未看出异样

 if (this.deviceIsAndroid) {
  layer.addEventListener('mouseover', this.onMouse, true);
  layer.addEventListener('mousedown', this.onMouse, true);
  layer.addEventListener('mouseup', this.onMouse, true);
 }
 layer.addEventListener('click', this.onClick, true);
 layer.addEventListener('touchstart', this.onTouchStart, false);
 layer.addEventListener('touchmove', this.onTouchMove, false);
 layer.addEventListener('touchend', this.onTouchEnd, false);
 layer.addEventListener('touchcancel', this.onTouchCancel, false);

具体的事件函数在前面被重写了,我们暂时不管他,继续往后面看先(话说,这家伙绑定的事件够多的)

stopImmediatePropagation

完了多了一个属性:

阻止当前事件的冒泡行为并且阻止当前事件所在元素上的所有相同类型事件的事件处理函数的继续执行.

如果某个元素有多个相同类型事件的事件监听函数,则当该类型的事件触发时,多个事件监听函数将按照顺序依次执行.如果某个监听函数执行了 event.stopImmediatePropagation()方法,则除了该事件的冒泡行为被阻止之外(event.stopPropagation方法的作用),该元素绑定的其余相同类型事件的监听函数的执行也将被阻止.

 <html>
     <head>
         <style>
             p { height: 30px; width: 150px; background-color: #ccf; }
             div {height: 30px; width: 150px; background-color: #cfc; }
         </style>
     </head>
     <body>
         <div>
             <p>paragraph</p>
         </div>
         <script>
             document.querySelector("p").addEventListener("click", function(event)
             {
                 alert("我是p元素上被绑定的第一个监听函数");
             }, false);
             document.querySelector("p").addEventListener("click", function(event)
             {
                 alert("我是p元素上被绑定的第二个监听函数");
                 event.stopImmediatePropagation();
                 //执行stopImmediatePropagation方法,阻止click事件冒泡,并且阻止p元素上绑定的其他click事件的事件监听函数的执行.
             }, false);
             document.querySelector("p").addEventListener("click", function(event)
             {
                 alert("我是p元素上被绑定的第三个监听函数");
                 //该监听函数排在上个函数后面,该函数不会被执行.
             }, false);
             document.querySelector("div").addEventListener("click", function(event)
             {
                 alert("我是div元素,我是p元素的上层元素");
                 //p元素的click事件没有向上冒泡,该函数不会被执行.
             }, false);
         </script>
     </body>
 </html>