第二十五課:jQuery.event.trigger的源碼解讀

本課主要來說解jQuery.event.trigger的源碼解讀。html

trigger = function(event, data, elem, onlyHandlers){node

  if(elem && (elem.nodeType === 3 || elem.nodeType ===8)){  //觸發的元素節點不能是文本節點和註釋節點數組

    return;瀏覽器

  }緩存

  var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType, type = event.type || event , namespaces =[];app

  .....性能

  if(type.indexOf(".") >= 0 ){  //若是事件類型帶點號,就表明有命名空間,因此須要分解出命名空間spa

    namespaces = type.split(".");代理

    type = namespaces.shift();htm

    namespaces.sort();

  }

  if((!elem || jQuery.event.customEvent [ type ]) && !jQuery.event.global[type]){  //若是元素不存在或者事件類型是自定義事件,而且以前歷來沒有綁定過此類型的事件,那麼就直接返回。

    return;

  }

  ....

  ontype = type.indexOf(":") < 0 ? "on" + type : ""; //若是事件類型沒有:字符,就證實是ontype綁定事件

  if(!elem){   //若是沒有指明觸發元素,就把整個緩存系統找一遍

    cache = jQuery.cache;

    for(i in cache){

      if(cache[i].events && cache[i].events[ type ]){   //從緩存系統中,找相對應的events屬性,並找到要觸發事件類型的events[type]

        jQuery.event.trigger(event,data,cache[i].handle.elem,true);     //觸發全部綁定了此事件類型的回調方法。

      }

    }

    return;   //直接返回

  }

  event.result = undefined;//清掉event的result屬性,方便重複使用。

  if(!event.target){

    event.target = elem;

  }

  data = data!=null ? jQuery.makeArray(data) :[];   //若是傳入了參數,就把參數轉換成數組類型

  data.unshift(event);   //把事件對象放入數組的第一項

  special = jQuery.event.special[type] || {};   //事件類型是否須要進行特殊化處理,好比:mousescroll事件

  if(special.trigger && special.trigger.apply(elem,data) === false){  //若是事件類型已經有trigger方法,就調用它,若是返回結果是false,就直接返回。

    return;

  }

  eventPath = [[elem, special.bindType || type]];  //規劃冒泡的路徑,從當前元素,一直到window

  if(!onlyHandlers && !special.noBubble && !jQuery.isWindow(elem)){ //若是元素的事件類型沒有阻止冒泡,而且元素不是window對象,就要進行冒泡模擬。

    bubbleType = special.delegateType || type;    //獲得事件的類型

    cur = elem.parentNode;

    for(old = elem; cur ; cur = cur.parentNode){   //把當前元素的全部父元素,都進行一次事件類型的冒泡。假設div1(當前元素,觸發click事件)上面有一個div2,div2上面有body,body上面有html,html上面有document。那麼eventPath的數組是:[[div1,click],[div2,click],[body,click],[html,click],[document,click]]

      eventPath.push([cur, bubbleType]);

      old = cur;

    }

    if(old === (elem.ownerDocument || document)){   //當old爲document時,cur爲空,就退出循環

      eventPath.push([old.defaultView || old.parentWindow || window, bubbleType]);   //模擬冒泡到window對象

    }

  }

  for(i=0;i<eventPath.length&&!event.isPropagationStopped(); i++){ //沿着上面規劃好的冒泡路線,把通過的元素節點的指定類型事件的回調逐一觸發執行

      cur = eventPath[i][0];   //元素

      event.type = eventPath[i][1]   //事件類型

      handle = (jQuery._data(cur,"events") || {})[event.type] && jQuery._data(cur,"handle");//先判斷在緩存系統中是否有此元素綁定的此事件類型的回調方法,若是有,就取出來。

      if(handle){   //執行這些回調方法

        handle.apply(cur,data);

      }

      handle = ontype && cur[ontype];    //若是有onXXX綁定的回調,不管是寫在js中,仍是html標籤內,都會取到

      if(handle && jQuery.acceptData(cur) && handle.apply && handle.apply(cur, data) === false){//若是handle不爲空,而且當前元素能綁定數據,而且handle有apply方法,就執行handle回調方法,若是當前回調返回false,就阻止默認事件。

        event.preventDefault();       

      }

  }

  event.type  = type;

  if(!onlyHandlers &&  !event.isDefaultPrevented()){  //若是沒有執行preventDefault方法,或上面的handle方法返回false(也會阻止默認事件)。就模擬默認行爲。具體是指模擬:submit,blur,focus,select,reset,scroll等方法。

    if((!special._default || special._default.apply(elem.ownerDocument,data) === false) && //若是用戶指定了默認行爲,就執行指定的默認行爲,若是指定的默認行爲返回false,就繼續判斷下面的條件

        !(type === "click" && jQuery.nodeName(elem, "a"))&&    //若是事件類型是click,而且是在a標籤上觸發的,就不執行它的默認行爲了,不然繼續判斷

          jQuery.acceptData(elem)){   //若是當前元素能夠綁定數據

            if(ontype && elem[type] &&  //若是元素有onXXX回調,而且元素有type方法,好比:<input type ="submit" onsubmit="function(){}">,它有onXXX的回調方法,也有input.submit的方法,所以繼續判斷

              ((type!=="focus" && type!=="blur") || event.target.offsetWidth !==0) && //若是事件類型不是focus,blur。或事件類型是focu,blur,可是觸發這個事件的元素不是隱藏的(隱藏的元素,它的offsetWidth爲0),就繼續判斷。(觸發隱藏元素的focus和blur默認行爲,IE6-8下會拋出錯誤。)

                !jQuery.isWindow(elem)){     //不是window元素就進入if語句執行默認行爲。(window的默認行爲觸發,會出現問題,好比:window.scroll方法,在IE與標準瀏覽器存在差別,IE會默認scroll()方法爲scroll(0,0))

                  old = elem[ontype];

                  if(old){

                    elem[ontype] = null;    //onXXX的回調已經執行過了,就不用再次執行了

                  }

                  jQuery.event.triggered = type;   //標識正在觸發此事件類型,防止下面的elem [ type ] ()重複執行dispatch方法

                  elem [ type ] ();   //執行默認行爲,好比:input[submit](),input元素的提交方法。點擊類型爲submint的input,會默認執行submit屬性方法。可是這裏不會調用dispatch方法。  

                  jQuery.event.triggered = undefined;  //還原

                  if(old){

                    elem[ontype] = old;

                  }

            }

    }

  }

  return event.result;

}

籠統來講,trigger就是dispatch的增強版。dispatch只觸發當前元素與其底下元素(經過事件代理的方式)的回調。trigger則是模擬整個冒泡過程,除了它自身,還觸發其祖先節點與window的同類型的回調。不過從trigger的代碼來看,它比dispatch多作的事就是觸發事件的默認行爲。其實trigger要作的事就是在某一元素觸發一個回調(dispatch),而後讓它順勢冒泡,觸發其餘回調(dispatch)就好了。

瀏覽器提供了原生派發機制,IE下叫fireEvent,標準瀏覽器爲dispatchEvent。IE下的問題是,有許多事件不能冒泡或不能冒泡到頂層,若是咱們把IE的代碼獨立出來,標準瀏覽器用一套,IE用一套(舊版本IE,IE9在標準模式中支持dispatchEvent方法了),這樣性能大大提升。zepto.js就是這麼幹的。

在建立萬能事件對象時,理應document.createEvent("Events");但早期瀏覽器須要用document.createEvent("HTMLEvents");

萬能事件理應能觸發全部事件的回調,可是鼠標事件,要用MouseEvents參數建立鼠標對象事件才行。

火狐不支持mousewheel,咱們須要用document.createEvent("MouseScrollEvents")建立DOMMouseScroll事件對象。

 

 

 

加油!

相關文章
相關標籤/搜索