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並不會影響綁定的其餘回調函數的執行
完整的看完代碼,深深感受到移動端的坑很是的多,頗有怪異的現象由於沒有遇到過暫時理解不了,但願以後能夠繼續研究,把代碼徹底讀懂。