前端事件系統(四)

事件派發


首先來看看jQuery.event的dispatch方法node

dispatch: function( event ) {

    // 對event對象進行修正
    event = jQuery.event.fix( event );

    var i, j, ret, matched, handleObj,
        handlerQueue = [],
        args = slice.call( arguments ),
        // 讀取事件緩存
        handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [],
        special = jQuery.event.special[ event.type ] || {};

    // 將參數一重置爲jQuery的事件對象
    args[0] = event;
    // 添加delegate屬性,用於事件代理
    event.delegateTarget = this;

    if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
        return;
    }

    // 取得事件隊列
    handlerQueue = jQuery.event.handlers.call( this, event, handlers );

    // 對於事件隊列的處理
    i = 0;
    while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
        event.currentTarget = matched.elem;

        j = 0;
        while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {

            // Triggered event must either 1) have no namespace, or 2) have namespace(s)
            // a subset or equal to those in the bound event (both can have no namespace).
            if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {

                event.handleObj = handleObj;
                event.data = handleObj.data;

                ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
                    .apply( matched.elem, args );

                if ( ret !== undefined ) {
                    if ( (event.result = ret) === false ) {
                        event.preventDefault();
                        event.stopPropagation();
                    }
                }
            }
        }
    }

    // Call the postDispatch hook for the mapped type
    if ( special.postDispatch ) {
        special.postDispatch.call( this, event );
    }

    return event.result;
},

通讀整段代碼,概括一下,jQuery.event.dispatch是作了一下幾項處理的jquery

  1. 對event對象進行修復
  2. 讀取事件句柄
  3. 取得事件隊列並對其進行處理

先看第一點緩存

對event對象的修復,這一步獲得event對象,是jQuery的event對象,而非原生的對象。這裏jQuery將本來只讀的對象,變爲了一個可讀可寫的對象,這樣就能夠對其進行隨意操做了。不過對於event對象修復,我打算現將其放到下一章與事件的修復一塊兒進行講解,所以這裏只須要知道,這裏是返回的jQuery的event對象就好了。app

第二點也再也不多說,就是讀取了事件的緩存函數

那麼來到第三點,也是事件分發的另一個重點post

事件隊列的獲取與處理。

在以前版本的jQuery中,隊列的生成與處理,都是放在了dispatch中進行,不過現在隊列已經交由jQuery.event.handlers生成並返回,那麼咱們首先來看下獲取到了handlerQueue究竟是什麼,即也就是對於jQuery.event.handlers來進行閱讀。this

jQuery.event.handlers


handlers: function( event, handlers ) {
    var i, matches, sel, handleObj,
        handlerQueue = [],
        delegateCount = handlers.delegateCount,
        cur = event.target;

    // 判斷是不是事件代理、與是不是鼠標左鍵的點擊事件
    if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {

        // 從事件源開始,遍歷所有祖先元素到綁定事件的元素爲止
        for ( ; cur !== this; cur = cur.parentNode || this ) {

            // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
            // 不對disabled的元素進行click的處理
            if ( cur.disabled !== true || event.type !== "click" ) {
                // 收集符合條件的事件句柄
                matches = [];
                for ( i = 0; i < delegateCount; i++ ) {
                    handleObj = handlers[ i ];

                    // 獲取selector
                    sel = handleObj.selector + " ";

                    if ( matches[ sel ] === undefined ) {
                        // 匹配事件句柄
                        matches[ sel ] = handleObj.needsContext ?
                        jQuery( sel, this ).index( cur ) >= 0 :
                            jQuery.find( sel, this, null, [ cur ] ).length;
                    }
                    if ( matches[ sel ] ) {
                        matches.push( handleObj );
                    }
                }
                if ( matches.length ) {
                    handlerQueue.push({ elem: cur, handlers: matches });
                }
            }
        }
    }

    // 在事件隊列中,增長其餘直接綁定的事件句柄
    if ( delegateCount < handlers.length ) {
        handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
    }

    return handlerQueue;
}

這一部分,總體看來比較複雜,咱們來理一下一下。spa

這部分先對事件代理作了判斷並進行了處理,採用match來對符合條件的事件句柄作一個篩選,並將全部符合條件的事件句柄,按從深及淺的順序,一一放入了事件隊列之中。代理

而後,在處理完成了事件代理以後,採用delegateCount區分事件代理以及直接綁定,再將直接綁定的事件句柄,放入事件隊列之中,生成了最終的事件隊列。這樣,最終獲得的,就是一個委託層次越深,便越會提早執行的事件隊列。code

所以,事件委託,在這一步就已經完成了。同時,由於jQuery的事件處理機制,是這樣一個隊列的形式,所以,以前在第一章末尾所說起的,對於執行順序的問題,這裏也很好的解決了。

再回來看dispatch對於事件隊列的處理


那麼最後,咱們來看dispatch中如何對於這個事件隊列進行的處理。

// 對於事件隊列的處理
i = 0;
while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
    event.currentTarget = matched.elem;

    j = 0;
    while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {

        if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {

            event.handleObj = handleObj;
            event.data = handleObj.data;

              //執行事件回調函數
            ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
                .apply( matched.elem, args );

              //直接return false,便可event.preventDefault以及stopPropagation
            if ( ret !== undefined ) {
                if ( (event.result = ret) === false ) {
                    event.preventDefault();
                    event.stopPropagation();
                }
            }
        }
    }
}

這一部對於事件隊列進行了有序的執行(由深及淺再到自己),而後,在這個過程當中,經過已經修正過的jQuery事件對象,動態的改變event對象的屬性,在執行事件句柄。同時,也對return false後,直接調用event.preventDefault(),與event.stopPropagation()進行了處理。

總結:


那麼,到目前,對於事件綁定這一塊,除了對於事件的修復部分,其餘的部分都已經閱讀完畢。咱們到最後再來理一下整個的過程。

  1. 首先,綁定時,採用了on方法,在這個過程當中,on對於咱們綁定時候,所傳進來的參數,一一進行了處理,並最終將其傳入jQuery.event.add中來執行。
  2. event.add部分,引用了dispatch方法進行了事件分發,並將事件名與事件句柄進行了填充
  3. dispatch部分,對於event對象進行修復,讀取事件句柄,同時取得事件隊列並對其進行處理,並執行回調。

那麼下一章,將對jQuery事件對象的修復,以及事件的修復,進行一個講解。

相關文章
相關標籤/搜索