Patience and perseverance will get paid.
javascript
这段时间开始实习了,在公司作hybrid
,专职写js,学习到了很多东西。一直好奇fastclick是如何工做,因而花了几天空余的时间一步步调试代码,学习fastclick。这篇文章能够结合者代码看,但愿能够给予须要学习fastclick的人一点思路。java
有错误的地方但愿指正,thk~浏览器
FastClick.attach()ide
FastClick(layer)函数
初始化化变量学习
this.trackingClick = false; //追踪一个click this.trackingClickStart = 0; //追踪时间 this.targetElement = null; // 目标元素 this.touchStartX = 0;// X坐标 this.touchStartY = 0;// y坐标 this.lastTouchIndentifier = 0; this.touchBoundary = 10;//边界条件(是不是一个点击) this.layer = layer;//layer能够是document.body/document.documentElement
安卓设备绑定鼠标事件(在捕获阶段,为的是第一时间处理到事件)ui
layer.addEventListener('mouseover',bind(this.onMouse,this),true); layer.addEventListener('mousedown',bind(this.onMouse,this),true); layer.addEventListener('mouseup',bind(this.onMouse,this),true);
绑定touch和click事件(断定是不是click行为,取消以前的click),this
//最早捕获到 layer.addEventListener('click', bind(this.onClick, this), true); //冒泡阶段捕获 layer.addEventListener('touchstart', bind(this.onTouchStart, this), false); layer.addEventListener('touchmove', bind(this.onTouchMove, this), false); layer.addEventListener('touchend', bind(this.onTouchEnd, this), false); layer.addEventListener('touchcancel', bind(this.onTouchCancel, this), false);
判断是否存在stopImmediatePropagation
,若是不存在则进行hack,在onMouse
中会利用stopImmediatePropagation
来阻止其余点击事件的回调函数的执行,避免ghost click
的现象spa
onMouse中,防止点透等诡异现象的代码调试
if (!this.needsClick(this.targetElement) || this.cancelNextClick) { // Prevent any user-added listeners declared on FastClick element from being fired. if (event.stopImmediatePropagation) { event.stopImmediatePropagation(); } else { // Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2) event.propagationStopped = true; } // Cancel the event event.stopPropagation(); event.preventDefault(); return false; }
判断是否经过onclick
绑定了回调函数,若是有读取出来,使用addEventListener
,绑定事件处理函数
if (typeof layer.onclick === 'function') { // Android browser on at least 3.2 requires a new reference to the function in layer.onclick // - the old one won't work if passed to addEventListener directly. oldOnClick = layer.onclick; layer.addEventListener('click', function (event) { oldOnClick(event); }, false); layer.onclick = null; }
当一些更高级别的事件发生的时候(如电话接入或者弹出信息)会取消当前的touch操做,即触发ontouchcancel。通常会在ontouchcancel时暂停游戏、存档等操做。所以在调试的时候才会在touchStart以后,就触发了touchCancel
直接断点touchend
,在控制台打印,能够看到touchstart
也是触发的了、
判断是否是单点触发
if (event.targetTouches.length > 1) { return true; }
获取目标对象和touch
事件对象
targetElement = this.getTargetElementFromEventTarget(event.target); touch = event.targetTouches[0];
根据touch
事件对象,设置一些初始属性
this.trackingClick = true; //标识跟踪该次点击 this.trackingClickStart = event.timeStamp;//点击开始的时间 this.targetElement = targetElement;//目标元素 this.touchStartX = touch.pageX; //x坐标 this.touchStartY = touch.pageY; //y坐标
若是刚触发完touchstart事件立刻就触发touchend,说明手指只是轻轻点了一下屏幕,也就是所谓的点击操做。这样即便不监听click事件也能实现点击的侦听。不过这里有一个实际的状况,不少山寨的Android设备屏幕很不灵敏,须要使劲按下才能有所感知。这种状况下必定会触发touchmove事件。因此针对Android设备的点击操做能够适当放宽,好比touchstart和touchend之间能够容许有少许几个touchmove,而且touchmove的距离不能超过多少个像素等等
所以也是须要监听onTouchMove,而且加入判断
// If the touch has moved, cancel the click tracking if ( this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event) ) { this.trackingClick = false; this.targetElement = null; }
在touchend的时候,执行this.onTouchEnd(上个流程绑定了)
判断是否在追踪该click,在this.onTouchMove的时候,若是移动的距离大于边界,则将this.trackingClick=false,在touchend就不用再判断是否为一个click的行为
if(!this.trackingClick){ return true; }
获取目标元素标签,须要根据标签名来作一些判断
targetTagName = targetElement.tagName.toLowerCase();
若是是label
,进行bug修复
执行this.needsFocus
,针对表单元素的focus和click事件的处理
先focus表单
在触发点击事件
针对IOS,滚动层bug修复
判断元素是否须要原生的click,实际上就是有些行为仍是要浏览器来执行默认的行为
表单元素disabled
,点击不了
type=file
的控件
video
label
若是不须要,则发送一个click事件
event.preventDefault(); this.sendClick(targetElement, event);
在一些安卓设备上,必须让一个元素blured
,才能使建立的clickEvent
生效
if (document.activeElement && document.activeElement !== targetElement) { document.activeElement.blur(); }
建立clickEvent
,使用touch
事件对象的属性来进行初始化
clickEvent = document.createEvent('MouseEvents'); clickEvent.initMouseEvent( this.determineEventType(targetElement), //bug修复针对select true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
建立完成以后,赋予对象一个额外的属性,在onClick
中可使用,而后触发点击事件,此时经过addEventListner
绑定的click
事件就会触发
clickEvent.forwardedTouchEvent = true; targetElement.dispatchEvent(clickEvent);
addEventListener添加会按照添加顺序执行
onClick做为第一个注册监听的,所以,是第一个执行的click
事件的回调函数
特殊状况处理,通常不会执行
/* It's possible for another FastClick-like library delivered with third- party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early. */ if (this.trackingClick) { this.targetElement = null; this.trackingClick = false; return true; }
特殊状况处理
if (event.target.type === 'submit' && event.detail === 0) { return true; }
执行onMouse
,
//建立时,附带的一个属性 if (event.forwardedTouchEvent) { return true; }
最后返回为真
return permitted; //true
注意:在这里的return
的true或false并不会影响绑定的其余回调函数的执行
完整的看完代码,深深感受到移动端的坑很是的多,颇有怪异的现象由于没有遇到过暂时理解不了,但愿以后能够继续研究,把代码彻底读懂。