第二十四課:事件系統3

來源:互聯網
上載者:User

標籤:

本課還是來講解一下jQuery是如何?它的事件系統的。上一課主要講了jQuery.event.add的源碼解析,這幾課主要針對jQuery源碼進行分析,如果不需要理解jQuery是如何?事件系統機制的,可以忽略。這一課我們先來講一下jQuery.event.remove的源碼解讀。

remove方法的目的是,根據使用者傳參,找到事件隊列,從裡面把匹配的handleObj對象移除,在參數不足的情況下,可能移除多個或所有的handleObj。當隊列的長度為0(當某事件的事件處理函數數組為空白時,就代表此類型事件沒有事件處理函數了,因此移除此事件)時,就移除相應的事件,當events為空白對象(events是元素繫結的所有事件類型的集合,當它為空白時,就代表元素沒有綁定任何事件,那麼就沒有必要再儲存元素在緩衝系統的唯一標識了)時,就清除掉UUID(也就是元素在緩衝系統中唯一的標識)。

接下來,我們來看它的源碼:

remove = function(elem,types,handler,selector){

  var t,tns,type,origType,namespaces,origCount,j,events,special,eventType,handleObj,

    elemData = jQuery.hasData(elem) && jQuery._data(elem);

  if(!elemData || !(events = elemData.events)){   //如果緩衝系統中沒有有關這個元素的資料,或者不支援添加自訂屬性(jQuery._data(elem))就直接返回。如果支援添加自訂屬性,並且緩衝系統中有這個元素的資料,但如果有關這個元素的事件處理函數為空白,也直接返回,因為沒有什麼可以移除了,此元素本身就沒有綁定任何事件處理函數。

    return;

  }

  types = jQuery.trim(hoverHack(types||"")).split(" ");   //處理hover事件的,它需要轉換成mouseenter和mouseleave兩個事件

  for(t=0;t<types.length;t++){

    ....

    type = origType = types[t];

    ....

    if(!type){   //如果沒有指定需要移除的事件類型,那麼就移除所有事件類型或所有與此命名空間有關的事件類型

      for( type in events){

        jQuery.event.remove(elem,type,handler,selector,true);

      }

      continue;

    }   

    special = jQuery.event.special[type] || {}; //處理特殊的事件類型,並不是所有的事件都能直接使用,比如Firefox下沒有mousewheel,需要用DOMMouseScroll冒充

    type = (selector ? special.delegateType: special.bindType) || type; //對於事件代理的綁定,需要針對不能冒泡的事件進行處理,取得真正用於綁定的事件類型

    eventType = events[type] || []; //取得此事件類型的裝載事件描述對象的數組

    origCount = eventType.length;    

    for(j=0;j<eventType.length;j++){   //移除數組中合格事件描述對象

      handleObj = eventType[j];

      if(origType===handleObj.origType&&   //比較事件類型是否一致

        !handler || handler.guid === handleObj.guid&&  //如果沒有傳入事件處理函數,或者傳入了,並且guid等於事件描述對象的guid,就繼續判斷

          !namespace || namespace.test(handleObj.namespace) && //如果types有命名空間,就判斷他們的命名空間是否一樣

            !selector || selector === handleObj.selector //如果是事件代理,就判斷事件代理是否一致,

      ){

        eventType.splice(j--,1);   //移除這個事件描述對象

        if(handleObj.selector){   //如果是事件代理的事件描述對象被移除,就把delegateCount減一

          eventType.delegateCount--;

        }

        .....

      }

    }

    if(eventType.length === 0 && origCount ! ==eventType.length){//如果事件描述對象數組為空白,並且是因為上面for迴圈移除之後才為空白的,就進入if語句。之所以加上後面的那個判斷,是因為eventType本身如果是空的,並不是通過for迴圈移除的,那進入到if語句,會出現死迴圈

      .....

      delete events[type];   //刪除此類型事件的屬性。因此此類型事件沒有事件處理函數了,被全部刪除了

    }

  }    

  if(jQuery.isEmptyObject(events)){//如果此元素的所以事件類型的屬性都被刪除了,那麼events將是一個Null 物件

      delete elemData.handle;     //既然此元素的所有事件類型都沒有了,就證明此元素沒有綁定任何事件,那麼也要刪除通過add添加給它的回調方法(此回調方法會調用此元素的所有回調方法)。

      jQuery.removeData(elem,"events",true);   //移除這個元素在緩衝系統中的events空間(清楚這個元素在緩衝空間的UUID)。

  }

}

接下來,我們來講一下jQuery.event.dispatch的源碼:

jQuery就是利用這個dispatch方法,從緩衝體中的events對象取得對應隊列,然後修複事件對象event,傳入使用者的回調中執行,根據回調的傳回值決定是否停止迴圈,是否阻止預設行為和事件冒泡。

dispatch = function(event){

  event = jQuery.event.fix(event || window.event);    //解決W3C和IE事件對象的差異,比如:W3C,event.target等於IE的event.srcElement

  var i,j,cur,ret,selMatch,matched,matched,handleObj,sel,related,

    handlers = ((jQuery._data(this,"events") || {}) [event.type]  || [],  //得到緩衝系統中this(操作的元素)的events對象,然後取它的事件類型(你觸發的event的類型)屬性對象,這個屬性對象就是裝有事件描述對象的數組。

      delegateCount = handlers.delegateCount,  //得到這個事件描述對象數組有多少個使用了事件代理

        args = core_slice.call(arguments),   //[].slice.call(arguments),轉換成數組

          ...

          special = jQuery.event.special[event.type]  || {},   //解決特殊的事件類型

            handlerQueue = [];

  ....

  if(delegateCount && !(event.button && event.type === "click")){  //這裡先講一下event.button這個屬性,這個屬性,代表使用者點擊了滑鼠的左鍵還是右鍵,還是捲軸,等等。IE與標準瀏覽器的值不一樣,比如,左鍵IE下是1,而其他瀏覽器是0等。但是在上面的代碼中已經修複了event對象,因此IE下,點擊左鍵也就是0.因此上面的if語句的意思是:如果有事件代理,並且是左鍵點擊(event.button=0),就進入if語句。

    for(cur = event.target ; cur != this;cur = cur.parentNode || this){ //從事件來源開始,遍曆其所有的祖先,一直到綁定事件的元素

      if(cur.disabled !==true || event.type !== click){//如果元素是被禁止的,並且事件類型是click,就不進入if語句。意思就是不能在被禁止的元素上觸發click事件

        ....

        matches = []; //用於收集合格事件描述對象

        for(i=0;i<delegateCount;i++){  //使用事件代理的事件描述對象總是排在數組前面

          handleObj = handlers[i];

          sel = handleObj.selector;  //擷取這個事件描述對象的執行內容

          if(selMatch[sel] ===undefined) {  //第一次時,selMatch[sel]是undefined

            selMatch[sel] =  jQuery.find(sel,this,null,[cur]).length;  //sel,當前事件描述對象的執行內容,this事件觸發的元素,cur當前遍曆的元素。如果cur當前遍曆的元素匹配,就為真,不匹配就為假

          }

          if(selMatch[sel]){  //如果當前元素在事件描述對象中匹配

            matches.push(handleObj);  //就把這個事件描述對象收集起來。有多少元素在事件描述對象數組中匹配,就收集多少個事件描述對象。

          }

        }

        if(matches.length){

          handlerQueue.push({elem:cur,matches:matches});//如果當前遍曆的元素,匹配事件描述對象數組中的一個或以上。就把這些組裝成一個jsonObject Storage Service起來。

        }

      }

    }   

  } 

  if(handlers.length > delegateCount){   //如果有不是事件代理的事件描述對象,將把這些事件描述對象與綁定事件的元素組裝成json,儲存起來

    handlerQueue.push({elem:this,matches:handlers.slice(delegateCount)});

  } 

  for(i=0;i<handlerQueue.length && !event.isPropagationStopped();i++){   //遍曆handlerQueue數組,條件是event沒有停止冒泡。

    matched = handlerQueue[i];

    event.currentTarget = matched.elem;  //事件冒泡到的當前目標元素。

    for(j=0;j<matched.matches.length && !event.isImmediatePropagationStopped();j++){//遍曆當前目標元素中所有與event.type同類型的事件描述對象數組。條件是使用者沒有執行停止冒泡的方法。(使用者調用stopImmediatePropagation方法,isImmediatePropagationStopped方法就會返回true)

      handleObj = matched.matches[j] ;   //取到這個事件描述對象

      .....

      event.data = handleObj.data;

      event.handleObj = handleObj;   //把事件描述對象賦給事件的handleObj屬性

      ret =( (jQuery.event.special[handleObj.origType] || {}).handle || handleObj.handler ).apply(matched.elem,args) ;// 先看此事件描述對象中的事件類型是否是特殊事件,如果是,就調用它的handle方法。如果不是,就直接呼叫事件描述對象的handler方法。執行內容是在當前遍曆的目標元素中。

      if(ret!==undefined){

        event.result = ret;  //如果事件處理函數執行後,傳回值不是undefined,就賦給event的result屬性

        if(ret === false){  //如果事件處理函數返回false

          event.preventDefault();   //阻止預設事件

          event.stopPropagation();  //停止冒泡

        }

      }

    }

  }

  return event.result;   //返回事件處理函數執行後的傳回值。

} 

dispatch的痛點在於,類比事件傳播的機制,其實就是事件冒泡的機制。 

 

 

加油!

第二十四課:事件系統3

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.