jQuery爲咱們提供了一個很是豐富好用的事件API,相對於瀏覽器自身的事件接口,jQuery有如下特色:jquery
1. 對瀏覽器進行了兼容性處理,用戶使用不須要考慮瀏覽器兼容性問題正則表達式
2. 事件數據是保持在內部緩存中的,而不是保持在DOM節點上json
3. 事件委託機制,提供了一個很是簡單的事件委託使用方法數組
4. 自定義事件,不單單是瀏覽器事件,能夠建立自定義事件瀏覽器
5. 輔助功能,好比命名空間,事件數據等等緩存
那麼下面就來看看jQuery是怎麼實現的,首先掃一眼Event模塊的源碼結構:app
總共900行,總共包括5部分:ide
1. 正則和輔助函數:最上面的正則表達式和3個輔助函數,後面會說到函數
2. event輔助類:用於事件處理的輔助對象ui
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); }); },
這段代碼實際上是對用戶調用的方法進行了各類參數狀況邏輯判斷,最後歸根到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對象數組中的第一個對象,而且不執行冒泡,不執行瀏覽器默認事件。