前端事件系統(三)


上一章對於jQuery的事件系統,對於jQuery的一些事件綁定接口作了分析。同時,引入了事件委託的概念。那麼,從本章起,將開始深刻到jQuery的事件系統內部,對於其源碼進行解析。html

這一篇是能夠獨自拿出來看,與前面兩章雖然有些關係,可是若是隻是對於jQuery源碼有興趣的,而且對前端事件有些理解的,從這章開始看也是能夠的。前端

前端事件系統(一)jquery

前端事件系統(二)api


on方法

上一章說起到jQuery綁定的核心,是落在了on方法身上的。on方法至關於對於jQuery的事件接口進行了統一,讓jQuery其餘事件綁定有關的方法,直接調用on方法來進行實現。所以,咱們將拿on方法做爲jQuery事件一開始的一個突破口,來對jQuery事件進行層層分析。下面來看jQuery的on方法數組

on : function( types, selector, data, fn, /*INTERNAL*/ one ) {
    var origFn, type;

    // Types can be a map of types/handlers
    /*
     * 第一個參數傳入爲對象的處理,相似
     * {「event1name」:function(){},
     * "event2name":function(){}
     * }
     * 這樣的寫法。
     * 而後調用自身on方法,分別對其進行on方法綁定事件
     */
    if ( typeof types === "object" ) {
        // ( types-Object, selector, data )
        if ( typeof selector !== "string" ) {
            // ( types-Object, data )
            data = data || selector;
            selector = undefined;
        }
        for ( type in types ) {
            this.on( type, selector, data, types[ type ], one );
        }
        return this;
    }

    //對於data與fn爲null的狀況下的處理
    if ( data == null && fn == null ) {
        // ( types, fn )
        fn = selector;
        data = selector = undefined;
        //對於僅僅fn爲null的狀況下的處理
    } else if ( fn == null ) {
        if ( typeof selector === "string" ) {
            // ( types, selector, fn )
            fn = data;
            data = undefined;
        } else {
            // ( types, data, fn )
            fn = data;
            data = selector;
            selector = undefined;
        }
    }
    //若是執行函數直接是false的狀況的處理returnFalse便是直接return False的函數
    if ( fn === false ) {
        fn = returnFalse;
    } else if ( !fn ) {
        return this;
    }

    //若是最後一個參數爲1,那麼直接調用fn,並解綁。
    if ( one === 1 ) {
        origFn = fn;
        fn = function( event ) {
            // Can use an empty set, since event contains the info
            jQuery().off( event );
            return origFn.apply( this, arguments );
        };
        // Use same guid so caller can remove using origFn
        fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
    }
    //直接調用jQuery.event.add並傳入處理好的參數
    return this.each( function() {
        jQuery.event.add( this, types, fn, data, selector );
    });
}

能夠發現,做爲綁定核心的on方法,其實質上只是對傳進來的參數進行調整處理,並將處理事後的參數,傳入jQuery.event.add中來進行處理,所以,接下來咱們將對jQuery.event.add的源碼來進行分析與閱讀。瀏覽器


jQuery.event.add方法

這個方法比較長,而且內部有許多東西,暫時還很差去讀,所以,我將這一部分分爲幾個部分來進行閱讀。爲了方便理解,我將其分紅幾個部分,而後一一來解讀。我儘可能作到對這一部分的講解作到簡單易懂,解讀過程當中有不對的地方,還請指出。同時,這一部分的代碼,除非特別說明,不然所有來自於jQuery.event.add。緩存

綁定部分

首先,咱們先直接跳到事件註冊那一部分,即addeventListener,看看他作了些什麼app

// Init the event handler queue if we're the first
// 若是是第一次,那麼初始化事件隊列
if ( !(handlers = events[ type ]) ) {
    handlers = events[ type ] = [];
    handlers.delegateCount = 0;

    // Only use addEventListener if the special events handler returns false
    // 若是獲取特殊事件失敗,那麼採用addEventListener來進行事件註冊
    if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
        if ( elem.addEventListener ) {
            // 註冊時將eventHandle來進行註冊,而且僅僅執行一次
            elem.addEventListener( type, eventHandle, false );
        }
    }
}

這段代碼,實際就是對於handlers的初始化,同時,對事件採用addEventListenr進行了綁定。dom

在這一段代碼中,咱們能夠看出,jQuery在綁定事件時,與如下幾個部分有關:函數

  • eventHandle
  • events
  • handlers
  • special

下面將對這幾個部分,以及這幾個部分衍生出來的部分一一進行解讀。同時,如下幾部分代碼,爲了方便閱讀,對自己這部分的代碼進行了一些抽象(這詞用到這裏怎麼怪怪的),方便於理解

eventHandle部分

//獲取數據緩存
var elemData = data_priv.get( elem );
...
// 若是elemData沒有handle屬性,給其初始化一個handle屬性
if ( !(eventHandle = elemData.handle) ) {
    eventHandle = elemData.handle = function( e ) {
        // Discard the second event of a jQuery.event.trigger() and
        // when an event is called after a page has unloaded
        return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ?
            jQuery.event.dispatch.apply( elem, arguments ) : undefined;
    };
}

這裏首先對於咱們要操做的結點下的數據緩存進行了獲取,同時,將數據緩存下的handle屬性,賦給了eventHandle。這裏能夠發現,elemData.handle這一部分,並無直接對於事件的回調函數進行處理,而是進一步的使用jQuery.event.dispatch來進行的處理。

jQuery.event.dispatch這裏還不會進行閱讀,咱們只要知道,jQuery對於回調的處理,和jQuery.event.dispatch這部分有關就好了。

events以及handlers部分

var //獲取數據緩存
     elemData = data_priv.get( elem ), 
     events;
...
// Init the element's event structure and main handler, if this is the first
// 若是elemData沒有events屬性,給其初始化一個events屬性
if ( !(events = elemData.events) ) {
    events = elemData.events = {};
}

events部分,來自於要操做節點下的數據緩存中的events屬性。而根據以前的操做

// Init the event handler queue if we're the first
// 若是是第一次,那麼初始化事件隊列
if ( !(handlers = events[ type ]) ) {
    handlers = events[ type ] = [];
     ...
}

能夠明白,events便是對於操做節點下各類事件的類型進行的記錄。

handlers部分

var handlers;
...
// Init the event handler queue if we're the first
// 若是是第一次,那麼初始化事件隊列
if ( !(handlers = events[ type ]) ) {
    handlers = events[ type ] = [];
    handlers.delegateCount = 0;
     ...
}
...
// Add to the element's handler list, delegates in front
// 將事件處理的對象加入處理列表
if ( selector ) {
    handlers.splice( handlers.delegateCount++, 0, handleObj );
} else {
    handlers.push( handleObj );
}

handlers實際上是做爲一個列表的做用,在裏面存放了handleObj的有關內容。

具體存入了什麼,咱們接下來來看handleObj具體是什麼。

handleObj部分

var handleObjIn,handleObj;
...
// Caller can pass in an object of custom data in lieu of the handler
// 對於像{handler:function(){},...}這樣的處理(即就是下面的handleObj的格式),後面可用於對handleObj的擴展
if ( handler.handler ) {
    handleObjIn = handler;
    handler = handleObjIn.handler;
    selector = handleObjIn.selector;
}
...
while ( t-- ) {
    ...
    // handleObj is passed to all event handlers
    // handleObj是做爲對事件處理的對象
    // 擴展handleObj的屬性
    handleObj = jQuery.extend({
        type: type,
        origType: origType,
        data: data,
        handler: handler,
        guid: handler.guid,
        selector: selector,
        needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
        namespace: namespaces.join(".")
    }, handleObjIn );
    ...
    // Add to the element's handler list, delegates in front
    // 將事件處理的對象加入處理列表
    if ( selector ) {
        handlers.splice( handlers.delegateCount++, 0, handleObj );
    } else {
        handlers.push( handleObj );
    }
    ...
}

handleObj即就是對於事件處理的一個對象,其包含了事件類型,初始類型,事件id,命名空間,dom元素,回調函數等屬性。

因此回到以前的handlers,其放入的內容,也就是事件處理對象。

那麼到了這裏,咱們對於jQuery.event.add方法已經有了一些頭緒了。那麼咱們來整理下到目前爲止的流程

流程圖

到這裏,add部分作了些什麼,咱們大概都清楚了。

再回到以前的代碼上,咱們能夠看到,其中的一部分是被while循環所包裹的,那這一部分是實現的什麼樣的內容呢?

多事件綁定與命名空間的處理

// Handle multiple events separated by a space
// rnotwhite = (/\S+/g)
// 將空格分開的types變爲數組
types = ( types || "" ).match( rnotwhite ) || [ "" ];

// 獲取事件類型的長度
t = types.length;
while ( t-- ) {
    //rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
    //對事件命名空間進行分割與獲取
    //例:click.myPlugin.simple
    tmp = rtypenamespace.exec( types[t] ) || [];
    //type則爲click
    type = origType = tmp[1];
    //namespaces則爲['myPlugin','simple']
    namespaces = ( tmp[2] || "" ).split( "." ).sort();
    ...
}

這裏,對於多事件(相似於("mouseover click"))進行了處理,而且經過while循環分別對其進行事件註冊。同時,while循環中對於事件的命名空間也進行了處理。

special部分

其實以前已經將這個列舉了出來,不過由於其並不影響以前的對於整個的閱讀,所以,將其拿到最後來講明。

var special;
...
while( t-- ) {
    ...

    // If event changes its type, use the special event handlers for the changed type
    // 若是事件改變了其type,採用特殊事件處理
    special = jQuery.event.special[ type ] || {};

    // If selector defined, determine special event api type, otherwise given type
    // 若是selector定義了,就採用special事件的type處理,不然直接採用type
    type = ( selector ? special.delegateType : special.bindType ) || type;

    // Update special based on newly reset type
    // 前面type重寫了,所以對special進行一次update
    special = jQuery.event.special[ type ] || {};
    ...
    // 特殊事件使用add對其進行處理
    if ( special.add ) {
        special.add.call( elem, handleObj );

        if ( !handleObj.handler.guid ) {
            handleObj.handler.guid = handler.guid;
        }
    }
    ...
}

由於瀏覽器兼容性的問題,對於一些事件並不能兼容。所以,對於這些事件,jQuery採用了special event的機制來對一些特殊的事件來進行處理,以達到跨瀏覽器支持事件的功能。


結語

距離上次寫第二篇到如今也很久了,人仍是太懶了,寫的也很慢。這一章對於on與event.add方法進行了解讀,下一章將對於這一章所沒講到的事件委派來進行解析,嗯,仍是由於人懶,時間未知。

相關文章
相關標籤/搜索