首頁  >  文章  >  web前端  >  js原生實作FastClick事件的實例

js原生實作FastClick事件的實例

高洛峰
高洛峰原創
2016-12-06 13:34:191963瀏覽

註明:自己學習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(&#39;touchstart&#39;,handleEvent);
        targetList[i].addEventListener(&#39;touchend&#39;,handleEvent);
      }
    },
    unbind : function(query){
      var targetList = document.querySelectorAll(query);
      for(var i=0,len=targetList.length;i<len;i++)
      {
        targetList[i].removeEventListener(&#39;touchstart&#39;,handleEvent);
        targetList[i].removeEventListener(&#39;touchend&#39;,handleEvent);
      }
    } 
  }
  var touchX = 0 ,touchY = 0;
 
  function handleEvent(event){
    switch(event.type)
    {
      case &#39;touchstart&#39;:
        touchX = event.touches[0].clientX;
        touchY = event.touches[0].clientY;
        break;
      case &#39;touchend&#39;:
        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事件,透過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 &#39;touchstart&#39;:
        touchX = event.touches[0].clientX;
        touchY = event.touches[0].clientY;
        break;
      case &#39;touchend&#39;:
        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:&#39;fastclick&#39;,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(&#39;touchstart&#39;,handleEvent);
      targetList[i].addEventListener(&#39;touchend&#39;,handleEvent);
    }
  }
 
  Fastclick.unbind = function(query){
    var targetList = document.querySelectorAll(query);
    for(var i=0,len=targetList.length;i<len;i++)
    {
      targetList[i].removeEventListener(&#39;touchstart&#39;,handleEvent);
      targetList[i].removeEventListener(&#39;touchend&#39;,handleEvent);
    }
  }
  return fastclick;
}();

   

這個全域變數FastClick可以用來加入任意的fastclick事件。

下面來講講如何呼叫。

新增事件函數:

FastClick.addHandler('fastclick',function(event){});

刪除事件函數://匿名事件無法刪除

FastClick.removeHandler('fastclickHandler('fastclick);

綁定元素

FastClick.bind("div");

解綁

FastClick.unbind("div");

用這個方法,同樣需要我們在handtarler事件中對event. ,因為雖然這個方法可以加入多個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事件。


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn