一、tap.js
這是一個比較輕量的插件tap.js,142行代碼,支持模塊化開發。
1)handleEvent
addEventListener方法中的第二個參數,我原先並沒有注意到其實可以傳一個對象,該對象必是實現EventListener接口,查看在線代碼。
var tap = { handleEvent: function() { alert(this.name); }, name:'tap' }; document.getElementById('button').addEventListener('click', tap, false);
這樣一綁定點擊就能alert了。並且注意上面的“this”,“this”指向的是“tap”這個對象,而不是這個方法。這種寫法有幾種好處:
1. 可以使用“tap”對象中的屬性或方法了。
2. 動態地改變事件處理器,例如tap.handleEvent = xx.yy,而不用先removeEventListener再addEventListener了。
在插件的129行定義了“Tap.prototype.handleEvent”,在這個方法中通過“switch(e.type)”執行不同的事件。
2)自定義事件
插件中使用了兩種方式創建自定義事件,新方式CustomEvent與舊方式createEvent,查看在線代碼。
1. CustomEvent
//CustomEvent方式 var tap2 = document.getElementById('button2'); tap2.addEventListener('tap', function(e){ alert('custom ' + e.detail); }, false); var evt = new window.CustomEvent('tap', { bubbles: true, cancelable: true, detail: 'tap' }); tap2.dispatchEvent(evt);
“button2”就是個按鈕,綁定了個tap事件,注意不是綁定了這個后,點擊按鈕就會出現alert內容,如果想要點擊,還是需要綁定“click”事件的。
“detail”參數是指當事件初始化時傳遞的數據,可以傳遞任何值,“tap2.dispatchEvent()”方法返回一個布爾值。
如果想點擊按鈕觸發“tap”事件,可以下面這樣做:
tap2.addEventListener('click', function(e){ tap2.dispatchEvent(evt); }, false);
2. createEvent
var evt = document.createEvent('Event'); evt.initEvent('tap', true, true);
就創建自定義事件部分不一樣,其他地方都差不多。
插件在“Tap.prototype.end”方法中定義了事件,90行到108行。
3)使用方式
插件的prototype總共有方法“leftButton”、“start”、“move”、“end”、“cancel”、“destroy”和“handleEvent”。下面是demo代碼:
<div id="container"> <button id="button-1">Tap event</button> </div> <script> var container = document.getElementById('container'); var button1 = document.getElementById('button-1'); var tap = new Tap(container);//創建tap對象 button1.addEventListener('tap', callback, false); function callback (e) { e.preventDefault(); console.log('event: ' + e.type); } </script>
在PC上面使用,通過“mousedown”、“mousemove”與“mouseup”來模擬。
在移動端使用,是通過“touchstart”、“touchmove”、“touchend”與“touchcancel”來模擬。
1. tap構造函數
在“tap”構造函數中綁定了“touchstart”和“mousedown”事件,上面點擊按鈕產生的效果,其實是通過點擊“container”來實現的。
function Tap(el) { this.el = typeof el === 'object' ? el : document.getElementById(el); this.moved = false; //flags if the finger has moved this.startX = 0; //starting x coordinate this.startY = 0; //starting y coordinate this.hasTouchEventOccured = false; //flag touch event this.el.addEventListener('touchstart', this, false); this.el.addEventListener('mousedown', this, false); }
2. 事件處理方法“handleEvent”
上面的“this.el.addEventListener('touchstart', this, false); ”就用到了handleEvent概念來執行事件。
Tap.prototype.handleEvent = function(e) { switch (e.type) { case 'touchstart':this.start(e);break; case 'touchmove':this.move(e);break; case 'touchend':this.end(e);break; case 'touchcancel':this.cancel(e);break; case 'mousedown':this.start(e);break; case 'mouseup':this.end(e);break; case 'mousemove':this.move(e);break; } };
3. 觸發“tap”
在前面《觸屏touch事件記錄》中講到過,“touchstart”與“touchend”會成對出現,那么就會觸發插件中的“end”方法。而end方式就是執行“tap”的關鍵地方。
if (!this.moved) { try { evt = new window.CustomEvent('tap', { bubbles: true, cancelable: true }); } catch (e) { evt = document.createEvent('Event'); evt.initEvent('tap', true, true); } e.stopPropagation(); if (!e.target.dispatchEvent(evt)) { e.preventDefault(); } }
用到了自定義事件的概念,“e.target.dispatchEvent(evt)”這句判斷可以執行“e.preventDefault()”,這個地方可以有效的阻止“click”事件的發生,防止“點透”。
二、Zepto中的touch.js
Zepto是一個輕量級的針對現代高級瀏覽器的JavaScript庫, 它與jquery有着類似的api。而touch.js是其一個擴展包,用於操作移動端的相關手勢事件,兼容IOS、Android與Windows Phone。
1)Windows Phone指針事件
在源碼中有兩套Windows的指針事件,IE10中的MSPointerDown、MSPointerMove、MSPointerUp、MSPointerCancel與IE11中的pointerdown、pointermove、pointerup、pointercancel。
自 IE11 起,Microsoft 前綴版本的指針事件 API 不再受支持,查看指針事件,指針事件更新。
2)原理
與tap.js不同,它是將事件綁定在document中,並且比tap.js更完善,有長按,雙擊,上下左右划屏等,代碼長度也就100多行。
與tap.js一樣也是在touchend中自定義了“tap”事件,從touch.js中的119行開始。與tap.js一樣,也有X和Y的偏移距離判斷,這里是30以內,tap.js是10以內。
1 if (deltaX < 30 && deltaY < 30) { 2 tapTimeout = setTimeout(function() { 3 var event = $.Event('tap') 4 event.cancelTouch = cancelAll 5 touch.el.trigger(event) 6 7 if (touch.isDoubleTap) { 8 if (touch.el) touch.el.trigger('doubleTap') 9 touch = {} 10 }else { 11 touchTimeout = setTimeout(function() { 12 touchTimeout = null 13 if (touch.el) touch.el.trigger('singleTap') 14 touch = {} 15 }, 250) 16 } 17 }, 0); 18 }
上面代碼中3、4、5就是在執行“tap”事件了,而7-16行代碼是在執行雙擊或單擊。從上面的代碼中可以看出,會出現點透,因為並沒有阻止“click”事件的執行。
<div class="outter" id="outter"></div> <div class="layer" id="layer"></div> <script> $('#layer').on('tap', function() { var $this = $(this); $this.hide(); }); $('#outter').on('click', function() { alert('點透'); }); </script>
測試了下在UC中並沒有出現點透,但是在微信瀏覽器中出現了。如下圖所示,我點擊橙色區域,會觸發層下面綁定的“click”事件。
3)解決方法
1. 第一種有點暴力,既然沒有阻止默認行為,那我就在最后加一個阻止。
if (deltaX < 30 && deltaY < 30) { //..... e.preventDefault(); }
2. 第二種是在“tap”事件中做個異步,延遲300ms后再觸發,這樣的話“click”事件已經觸發了。
$('#layer').on('tap', function() { var $this = $(this); setTimeout(function() { $this.hide(); }, 300); });
三、Hammer.js實現tap
如果說tap.js是點心,touch.js是簡餐,那么hammer.js就是大餐了。2569行代碼,兼容各種瀏覽器,包括移動與PC端。
瀏覽器兼容性如下,查看更多兼容性。
實現的代碼中封裝了很多的邏輯,最后是將邏輯綁定到了“touchend”中,“touchstart”也綁定了,但是只執行了“touchend”事件。
關於這個插件的分析,篇幅比較長,可以參考《Hammer.js分析》的幾篇文章。
demo源碼下載:
http://download.csdn.net/download/loneleaf1/9429374
參考資料:
http://www.ayqy.net/blog/handleevent%E4%B8%8Eaddeventlistener/ handleEvent與addEventListener
https://github.com/memoryza/--/blob/master/zepto-tap%E7%82%B9%E9%80%8F.md zepto tap點擊穿透分析