徹底弄懂jQuery事件原理一


jQuery為我們提供了一個非常豐富好用的事件API,相對於瀏覽器自身的事件接口,jQuery有以下特點:

1. 對瀏覽器進行了兼容性處理,用戶使用不需要考慮瀏覽器兼容性問題

2. 事件數據是保持在內部緩存中的,而不是保持在DOM節點上

3. 事件委托機制,提供了一個非常簡單的事件委托使用方法

4. 自定義事件,不僅僅是瀏覽器事件,可以創建自定義事件

5. 輔助功能,比如命名空間,事件數據等等

那么下面就來看看jQuery是怎么實現的,首先掃一眼Event模塊的源碼結構:

總共900行,總共包括5部分:

1. 正則和輔助函數:最上面的正則表達式和3個輔助函數,后面會說到

 

2. event輔助類:用於事件處理的輔助對象

 

3. Event可寫事件對象,等同於瀏覽器事件中的event對象,但Event對象的數據是可寫的,添加了jQuery的一些屬性。

 

4. 兼容性處理:事件的兼容性處理邏輯

 

5. 對外API,添加到jquery實例對象的對外API

這段代碼結構邏輯上分為4層,

第一層是對外API,這段是對用戶調用的參數處理

第二層是event輔助類,用戶調用以后會調用輔助類的各種方法

第三層是Event對象,event處理過程中會創建Event對象來代替瀏覽器事件中的event對象

第四層是兼容性處理,針對瀏覽器中有些事件的兼容性問題,進行了處理 

 

1. 我們從對外API開始說起,比如:

<div id='div_main'>
    <div id="div_sub"></div>
</div>

<script>
    $("#div_main").on("click",function(){
        console.log(1);
    });
</script>

我們對$("#div_main")這個jquery對象調用了on方法,就可以注冊一個點擊事件,on方法是什么?見對外API那部分代碼

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

            // Types can be a map of types/handlers
            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;
            }

            if (data == null && fn == null) {
                // ( types, fn )
                fn = selector;
                data = selector = undefined;
            } 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;
                }
            }
            if (fn === false) {
                fn = returnFalse;
            } else if (!fn) {
                return this;
            }

            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++);
            }
            return this.each(function() {
                jQuery.event.add(this, types, fn, data, selector);
            });
        },
on

 

 這段代碼其實是對用戶調用的方法進行了各種參數情況邏輯判斷,最后歸根到jQuery.event.add方法,也就是最后是調用了event輔助類的方法,首先on方法對用戶傳入的參數進行了判斷,主要可能有以下幾種情況:

(1) 以json方式傳入多個事件方法,比如:

on({"click":fn1,"blur":fn2},"li",data);
on({"click":fn1,"blur":fn2},data);

        //json對象格式
        if (typeof types === "object") {
         //selector不是字符串是數據,則重新設置數據變量,on({"click":fn1,"blur":fn2},data)
if (typeof selector !== "string") { data = data || selector; selector = undefined; }
         //對每個json屬性遞歸調用on方法
for (type in types) { this.on(type, selector, data, types[type], one); } return this; }

(2)其他三種情況:on("click",fn) on("click","li",fn) on("click",data,fn)

            if (data == null && fn == null) {
                // 類似on("click",fn1),重置變量
                fn = selector;
                data = selector = undefined;
            } else if (fn == null) {
                if (typeof selector === "string") {
                    //類似on("click","li",fn) 
                    fn = data;
                    data = undefined;
                } else {
                    //類似on("click",data,fn);
                    fn = data;
                    data = selector;
                    selector = undefined;
                }
            }
       //快捷方式,如果fn參數傳入false,自動設置為false方法
if (fn === false) { fn = returnFalse; } else if (!fn) { return this; }
            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++);
            }
            return this.each(function() {//對每個jquery實例進行調用
                jQuery.event.add(this, types, fn, data, selector);
            });

然后是one方法,其實就是調用上面的on方法,帶上one參數

        one: function(types, selector, data, fn) {
            return this.on(types, selector, data, fn, 1);
        },

off方法,和on方法類似,針對輸入參數的幾種情況最終是調用了event輔助類的remove方法。

 off: function(types, selector, fn) {
            var handleObj, type;
            if (types && types.preventDefault && types.handleObj) {
                // ( event )  dispatched jQuery.Event
                handleObj = types.handleObj;
                jQuery(types.delegateTarget).off(
                    handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
                    handleObj.selector,
                    handleObj.handler
                );
                return this;
            }
            if (typeof types === "object") {
                // ( types-object [, selector] )
                for (type in types) {
                    this.off(type, selector, types[type]);
                }
                return this;
            }
            if (selector === false || typeof selector === "function") {
                // ( types [, fn] )
                fn = selector;
                selector = undefined;
            }
            if (fn === false) {
                fn = returnFalse;
            }
            return this.each(function() {
                jQuery.event.remove(this, types, fn, selector);
            });
        },

 

tigger方法,最終是調用event輔助類的tigger方法

        trigger: function(type, data) {
            return this.each(function() {
                jQuery.event.trigger(type, data, this);
            });
        },

triggerHandler方法,調用event類的方法

      triggerHandler: function(type, data) {
            var elem = this[0];
            if (elem) {
                return jQuery.event.trigger(type, data, elem, true);
            }
        }

tiggerHandler和tigger方法的區別是,triggerHandler只執行jQuery對象數組中的第一個對象,並且不執行冒泡,不執行瀏覽器默認事件。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM