註明:本人學習javascript時間不長,最近一直在做web端的手機網頁和應用,由於最近有用到類似fastclick的功能,在原來的程式中用touchstart和touchend事件類比,現在嘗試將其封裝,得到了以下兩種有問題的方案。分享給大家,另求大神指導
在手機端Web app開發中,click事件的300ms的延遲,會造成響應緩慢,尤其在低端機中尤為明顯。而使用touchstart或者touchend事件,會和預設的滾輪事件發生衝突,這也不是我們所期望的。
所以,自己動手,豐衣足食,寫了一個快速點擊事件的原生js代碼(考慮到web app開發的環境,我們暫時無需考慮對IE等瀏覽器的相容)。
實現方法1如下:
function FastClickEvent(handler){ var fastclick = { handler : handler, bind : function(query){ var targetList = document.querySelectorAll(query); for(var i=0,len=targetList.length;i<len;i++) { targetList[i].addEventListener('touchstart',handleEvent); targetList[i].addEventListener('touchend',handleEvent); } }, unbind : function(query){ var targetList = document.querySelectorAll(query); for(var i=0,len=targetList.length;i<len;i++) { targetList[i].removeEventListener('touchstart',handleEvent); targetList[i].removeEventListener('touchend',handleEvent); } } } var touchX = 0 ,touchY = 0; function handleEvent(event){ switch(event.type) { case 'touchstart': touchX = event.touches[0].clientX; touchY = event.touches[0].clientY; break; case 'touchend': var x = event.changedTouches[0].clientX; var y = event.changedTouches[0].clientY; if(Math.abs(touchX-x)<5||Math.abs(touchY-y)<5) fastclick.handler(event); break; } }; return fastclick;};
原理:根據連續touchstart和touchend事件發生時位置的變化,來判斷是否是一次點擊
調用:用一個handler函數來註冊一個FastClickEvent事件。然後將註冊好的FastClickEvent事件,通過bind方法,綁定到對應的元素上去。如下:
var handler = function(event){ console.log(event.target.id+" fastclicked");}var fastClick = new FastClickEvent(handler);fastClick.bind("div");
這段代碼,我們給所有的div元素註冊了fastclick的handler事件。調用fastClick.unbind來解除元素的綁定。
但是這段代碼有一個問題,為了讓handleEvent事件能夠訪問到touchX,touchY。我採用了閉包的手法,這意味著每次new一個FastClickEvent事件對象,都要在記憶體中再次注入重複的handleEvent函數。至於重複的touchX,touchY,更是不必多說了。
新手求助:原本是想把handleEvent函數寫到原型裡,但是產生的一個問題是handleEvent(event)的this對象是windows,也就是說,我取不到touchX和touchY以及handler對象,造成訪問錯誤。
有一種比較簡單的解決思路,就是只註冊一個fastClickEvent事件,然後在處理常式中根據event.target的實際值(即發生事件的對象上)來決定響應的內容。
但是,這意味著你必須對所有的fastclick事件都非常熟悉。
用這種方法帶來的好處在於,由於你只有一個handleEvent函數,所以基本來說,在頁面釋放之前,除非是你不想再觸發fastclick事件,否則無需去解除綁定任何元素的fastclick事件(即使你解除綁定了,記憶體中仍然存在該handler函數)。而且,你可以很方便的用bind(query)來添加任何動態產生的元素的fastclick事件,只要你在handler函數中已經寫好相應的處理常式。
如果你想添加多個fastclick事件,而且可能要在多個地方註冊,那麼也只要new一個新的FastClickEvent對象,然後綁定到對應的元素中去就可以了。
下面,介紹一種使用EventTarget類的方法。首先看一下EventTarget
function EventTarget(){ this.handlers = {};}EventTarget.prototype = { constructor: EventTarget, addHandler : function(type,handler){ if(typeof this.handlers[type] == "undefined"){ this.handlers[type]=[]; } this.handlers[type].push(handler); }, fire : function(event){ if(!event.target){ event.target = this; } if(this.handlers[event.type] instanceof Array){ var handlers = this.handlers[event.type]; for(var i=0,len=handlers.length;i<len;i++){ handlers[i](event); } } }, removeHandler : function(type,handler){ if(this.handlers[type] instanceof Array){ var handlers = this.handlers[type]; for(var i=0,len=handler.length;i<len;i++){ if(handlers[i]==handler){ break; } } handlers.splice(i,1); } }}
這個類,是一個用來添加、移除以及實現自訂類的介面。參考《JavaScript進階程式設計第三版》P616-617
那麼,如何把這個類,變成我們的fastclick事件介面呢?
定義一個全域變數,用這個變數來完成所有的fastclick事件註冊、刪除以及添加
var FastClick = function(){ var fastclick = new EventTarget(), touchX = 0 , touchY = 0; function handleEvent(event){ switch(event.type) { case 'touchstart': touchX = event.touches[0].clientX; touchY = event.touches[0].clientY; break; case 'touchend': var x = event.changedTouches[0].clientX; var y = event.changedTouches[0].clientY; if(Math.abs(touchX-x)<5||Math.abs(touchY-y)<5) fastclick.fire({type:'fastclick',target:event.target}); break; } }; fastclick.bind = function(query) { var targetList = document.querySelectorAll(query); for(var i=0,len=targetList.length;i<len;i++) { targetList[i].addEventListener('touchstart',handleEvent); targetList[i].addEventListener('touchend',handleEvent); } } Fastclick.unbind = function(query){ var targetList = document.querySelectorAll(query); for(var i=0,len=targetList.length;i<len;i++) { targetList[i].removeEventListener('touchstart',handleEvent); targetList[i].removeEventListener('touchend',handleEvent); } } return fastclick;}();
這個全域變數FastClick可以用來添加任意的fastclick事件。
下面來講講如何調用。
添加事件函數:
FastClick.addHandler('fastclick',function(event){});
刪除事件函數://匿名事件無法刪除
FastClick.removeHandler('fastclick',handler);
繫結元素
FastClick.bind("div");
解除綁定
FastClick.unbind("div");
用這個方法,同樣需要我們在handler事件中對event.target做預判,因為雖然這種方法可以添加多個fastclick事件,但是,事件在執行的過程中是按順序一個一個執行的,也就是說,可能會執行你並不想執行的函數。
帶來的好處在於,可以註冊多個fastclick事件,而且無需再次綁定,就可以執行了。
比如說,
FastClick.bind("div");FastClick.addHandler(handler1);FastClick.addHandler(handler2);
那麼,當快速點擊事件發生在任一div元素時,就會順序執行handler1和handler2。
如果我們調用removeHandler來刪除handler1或handler2,那麼相應的函數就不會再執行了。
另外,需要注意的是,在handler函數中,this對象是FastClick.handlers['fastclick']這個數組,一般情況下,我們用event.target來擷取發生事件的對象。
用這種方法,基本克服了上面方法的問題,而且,對這個對象重複new並沒有多大的意義,除非你不想對event.target做預判,從而產生一大堆的FaskClick類,但這顯然是不高效的。
新手求助:如何能夠實現特定的元素的綁定執行的函數,也就是: 能夠調用FastClick.bind(query,handler);實現對符合query條件的元素添加handler的fastclick事件。