jQuery 2.0.3 原始碼分析 事件體系結構
那麼jQuery事件處理機制能幫我們處理那些問題?
- 毋容置疑首先要解決瀏覽器事件相容問題
- 可以在一個事件型別上新增多個事件處理函式,可以一次新增多個事件型別的事件處理函式
- 提供了常用事件的便捷方法
- 支援自定義事件
- 擴充套件了組合事件
- 提供了統一的事件封裝、繫結、執行、銷燬機制
- ……
為了更深入的理解幕後的實現,所以先整理整體的結構思路,從1.7後就去除了live繫結,所以現在的整個事件的API
如圖:
jQuery的事件繫結有多個方法可以呼叫,以click事件來舉例:
- click方法
- bind方法
- delegate方法
- on方法
$('#foo').click(function(){ }) $('#foo').bind('click',function(){ }) $("foo").delegate("td", "click", function() { }); $("foo").on("click", "td", function() { });
以上四種繫結都能達到同一樣的效果,但是各自又有什麼區別,內部又是如何實現?
原始碼分析
click方式
jQuery.each( ("blur focus focusin focusout load resize scroll unloadclick
dblclick " + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { // Handle event binding jQuery.fn[ name ] = function( data, fn ) { return arguments.length > 0 ? this.on( name, null, data, fn ) : this.trigger( name ); }; });
原始碼很簡單,合併15種事件統一增加到jQuery.fn上
內部呼叫this.on / this
bind方式
bind: function( types, data, fn ) { return this.on( types, null, data, fn ); }, unbind: function( types, fn ) { return this.off( types, null, fn ); },
同樣呼叫的this.on/this.off
delegate方式
delegate: function( selector, types, data, fn ) { return this.on( types, selector, data, fn ); }, undelegate: function( selector, types, fn ) { // ( namespace ) or ( selector, types [, fn] ) return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); }
同樣呼叫的this.on/this.off
one方式
one: function( types, selector, data, fn ) { return this.on( types, selector, data, fn, 1 ); },
還是this.on
可見以上的介面只是修改了不同的傳遞引數,最後都交給on實現的
實現最簡單的事件委託
給父元素繫結事件,子元素也能響應
其實委託的原理都是一樣的,通過事件物件過濾出關聯目標的hack,做相對應的處理,那麼JQuery是如何實現的呢?
jQuery事件的流程圖
在繫結階段與執行階段
那麼JQuery為了更好的對事件的支援內部又做了哪些額外的優化操作?
相容性問題處理:
瀏覽器的事件相容性是一個令人頭疼的問題。IE的event在是在全域性的window下, 而mozilla的event是事件源引數傳入到回撥函式中。還有很多的事件處理方式也一樣
JQuery提供了一個 event的相容類方案
jQuery.event.fix 對遊覽器的差異性進行包裝處理
例如:
- 事件物件的獲取相容,IE的event在是在全域性的window,標準的是event是事件源引數傳入到回撥函式中
- 目標物件的獲取相容,IE中採用srcElement,標準是target
- relatedTarget只是對於mouseout、mouseover有用。在IE中分成了to和from兩個Target變數,在mozilla中 沒有分開。為了保證相容,採用relatedTarget統一起來
- event的座標位置相容
- 等等
事件的儲存優化:
jQuery並沒有將事件處理函式直接繫結到DOM元素上,而是通過$.data儲存在快取$.cahce上,這裡就是之前分析的貫穿整個體系的快取系統了
宣告繫結的時候:
- 首先為DOM元素分配一個唯一ID,繫結的事件儲存在$.cahce[ 唯一ID ][ $.expand ][ 'events' ]上,而events是個鍵-值對映物件,鍵就是事件型別,對應的值就是由事件處理函式組成的陣列,最後在DOM元素上繫結(addEventListener/ attachEvent)一個事件處理函式eventHandle,這個過程由 jQuery.event.add 實現。
執行繫結的時候:
- 當事件觸發時eventHandle被執行,eventHandle再去$.cache中尋找曾經繫結的事件處理函式並執行,這個過程由 jQuery.event. trigger 和 jQuery.event.handle實現。
- 事件的銷燬則由jQuery.event.remove 實現,remove對快取$.cahce中儲存的事件陣列進行銷燬,當快取中的事件全部銷燬時,呼叫removeEventListener/ detachEvent銷燬繫結在DOM元素上的事件處理函式eventHandle。
事件處理器:
jQuery.event.handlers
針對事件委託和原生事件(例如"click")繫結 區分對待
事件委託從佇列頭部推入,而普通事件繫結從尾部推入,通過記錄delegateCount來劃分,委託(delegate)繫結和普通繫結。
其餘一些相容事件的Hooks
fixHooks,keyHooks,mouseHooks
總的來說對於JQuery的事件繫結
在繫結的時候做了包裝處理
在執行的時候有過濾器處理
下章就開始深入on內部實現的分析了