【源碼小記】jQueryの事件綁定

前言

這篇筆記很大的幫助來源於慕課網上jQuery源碼解讀課程,雖然jQuery如今已經慢慢衰退了,可是做爲一個學習者,我本身仍是很願意去琢磨琢磨其中的原理。若是有錯誤歡迎指出,謝謝了^0^。node

事件綁定

你們確定都熟悉JavaScript中的瀏覽器事件模型——事件冒泡和事件捕獲。在DOM2級事件處理程序中,咱們可使用addEventListener方法來添加事件監聽,第三個參數能夠指定是捕獲階段(true)仍是冒泡階段(false),在IE中對應的方法是attachEvent,且只支持冒泡事件。數組

Q:爲何咱們要使用addEventListener?
咱們以往的寫法是XX.onclick=function(){},這樣不能綁定多個相同事件(會覆蓋),也不能選擇是在冒泡階段/捕獲階段。瀏覽器

Q:這樣的綁定可能會出現什麼問題?app

  1. 綁定的元素必須存在;
  2. 後期動態生成的HTML元素不會被自動加上綁定;
  3. 綁定過多會影響性能;
  4. 語法繁雜;

Q:什麼是事件委託?
注意看上面問題的第3點,假設咱們的頁面上有100個p標籤,那咱們就要添加100個事件監聽,這樣會影響頁面的運行性能。
可是若是咱們給body一個click監聽,當用戶點擊的頁面中的p標籤,實質上也是點擊body,因此body的這個click事件會從target開始進行層層冒泡,若是target就是咱們的目標元素,那麼就相應去運行咱們想調用的函數,這樣功效不也等同於監聽了p標籤嗎?
因此事件委託的定義是:函數

事件目標自身不處理事件,而是把處理任務委託給其父元素或者祖先元素,甚至根元素(document)

這樣一來,就算目標目前不存在它的容器裏也不會發生錯誤,也一樣減小了綁定次數。
因此模擬給a加上事件委託代碼以下:性能

(function() {
  var parent = document.getElementById('parent');
  //給祖先元素綁定監聽
  parent.addEventListener('click', handler, false);
 
  function handler(e) {
    //或許觸發冒泡的元素,若是元素是a,就作想作的事情
    var x = e.target;
    if (x.nodeName.toLowerCase() === 'a') {
      alert('a被點擊了!');
      e.preventDefault();
    }
  };
})();

jQuery的事件綁定方法

在jQuery中,與事件綁定相關的方法有如下幾種:學習

  1. 事件名方法(好比click())
  2. bind()
  3. delegate()
  4. on()
  5. live()

Tips:live()在1.9版本已被廢除。
而以上的幾種方法,bind() ,live() ,delegate()都是調用了on()來實現的,而歸根結底都是使用了addEventListener以及事件冒泡來實現的。ui

bind()

調用:this

$("#test").bind("click",function(){
  console.log(1);
});

源碼
圖片描述spa

bind()方法實際直接調用了on()方法,types是事件類型,data是傳入的要執行函數,更詳細的咱們一塊兒放到on()方法去看。可是先說清楚,它沒有用委託機制元素必需要存在

live()

調用:

$("#test").bind("click",function(){
  console.log(1);
});

源碼:
圖片描述

live()方法使用了事件委託機制,把全部的委託都交給了this.context(至關於document)來完成。

delegate()

調用:

$("body").delegate("#test","click",function(){
  console.log(1);
 })

源碼:
圖片描述

這裏看到其實delegate和live是相似的,只是live是this.context調用on方法,delegate是本身能夠指定委託對象,因此直接this調用on方法

on()

調用:

//直接調用
$("#test").on("click",function(){
  console.log(1);
 })
 
//委託調用 
$("body").on("click","#test",function(){
  console.log(1);
 })

源碼:
圖片描述
值得一提,one方法也是調用了on方法,不過它是隻調用一次事件,隨後就解綁。

/* on()方法,參數可能會錯位,因此須要處理
* elem: 被委託元素(調用on方法選擇元素) 
* types:事件類型(咱們舉例子傳入的都是click)
* selector:可選,真正的目標元素
* data: 可選,額外數據
* fn: 可選,監聽到後要調用的函數
* one:內部傳入,是不是one()方法調用的
*/
function on( elem, types, selector, data, fn, one ) {
    var origFn, type;

    //傳入的types多是一個數組(能夠同時綁定多個事件)
    if ( typeof types === "object" ) {
        //若是selector不是string,說明傳入了types,data
        if ( typeof selector !== "string" ) {
            //因此這修正一下data和selector的值
            data = data || selector;
            selector = undefined;
        }
        //遍歷每個事件,去分別添加綁定
        for ( type in types ) {
            on( elem, type, selector, data, types[ type ], one );
        }
        //返回elem,以支持鏈式調用
        return elem;
    }
    
    //若是隻傳了三個參數,說明是傳入了types,fn
    if ( data == null && fn == null ) {

        // 把參數歸位
        fn = selector;
        data = selector = undefined;
    } else if ( fn == null ) {
        // 若是隻有fn是null,且selector是string,說明是傳入了types,fn,selector
        if ( typeof selector === "string" ) {
            //參數歸位
            fn = data;
            data = undefined;
        } else {
            //不然是傳入了types,data,fn
            fn = data;
            data = selector;
            selector = undefined;
        }
    }
    
    if ( fn === false ) {
        fn = returnFalse;
    } else if ( !fn ) {
        return elem;
    }
    
    //若是是one()方法
    if ( one === 1 ) {
       //保存要調用的函數
        origFn = fn;
        fn = function( event ) {

            //解綁事件
            jQuery().off( event );
            //調用函數
            return origFn.apply( this, arguments );
        };

        //給origFn和fn使用一樣的記錄函數的guid,方便移除
        fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
    }
    
    //給每一個元素註冊事件處理程序
    return elem.each( function() {
        jQuery.event.add( this, types, fn, data, selector );
    } );
}

這裏的guid和event.add()就涉及到了jQuery的事件機制,比較複雜,因此另再開坑來學習。

相關文章
相關標籤/搜索