本章將把重點放在於對於事件的委託機制,以及jquery的事件繫結方法做一些解析。本章並沒有什麼比較難懂的地方,也還沒有深入到jQuery的事件系統內部。
事件委託
上一章講了前端事件系統以及簡單地對各個瀏覽器進行相容的方法。對於要求不高的頁面來說,之前的簡單事件註冊,就可以很好的勝任各種各樣的工作了。但是,試想一種情況。倘若一個頁面有著極大量的事件繫結的需求,那麼我們之前的事件系統,就不得不一個個的去繫結事件。這樣對效能來說,肯定是一種災難,同時,動態增加的節點無法完成繫結的工作。因此,我們需要引入事件委託這一機制。
冒泡與捕獲
我們知道dom的事件處理,分為捕獲階段,目標階段,以及冒泡階段三個部分。那麼簡單地講一下這三個階段吧,當我們點選了一個a標籤的時候,整個事件的流程大致是下面的階段
首先事件從dom樹向下傳送,逐個訪問目標節點的祖先節點,同時將會對該事件的捕獲事件監聽器來進行檢測,並執行,直到訪問到目標節點,這一階段就是我們說的捕獲階段;到達目標節點(即a標籤)後,就會執行該事件監聽器,這一階段也就是目標節點;最後事件將會從目標節點開始,從dom樹往上傳送,再依次訪問目標節點的祖先節點,並且執行對應的非捕獲事件的事件監聽器,並且執行
委託的原理
既然有了冒泡和捕獲的概念,那麼事件委託的原理也很好理解了。事件委託利用了事件可以傳播的這一特性,並不使用事件本身對於事件來進行處理,而是將對事件的處理任務交予了其祖先節點來進行處理。
這樣做,減少了對於頁面上的dom操作。比如你對ul下的li繫結事件,通過事件委託,你只需要對於ul繫結一次事件,然後使用事件傳播的這一特性,li的事件來進行工作,這樣,極大地減少了繫結量,而且即使是動態增加的li節點,也同樣是可以去執行該事件的。
jQuery的事件繫結
那麼終於來到了jQuery的部分。從這裡開始也將對jQuery的事件部分,盡個人所能來做一個解析,之後出現jQuery的原始碼的版本為2.1.4,解析過程中有不對的地方,歡迎打臉。
jQuery的事件繫結,有以下幾種方法:
- 直接用click,blur等事件名的方法
- bind方法
- delegate
- live方法
- one方法
- on方法
那麼我們對上面幾種方法來一一分析(本章暫不會對作為核心的on的原始碼來進行分析)
1.直接用事件名進行事件繫結即是直接採用類似.click(),.blur()的方式進行繫結那麼jQuery中是如何做到的呢
1 2 3 4 5 6 7 8 9 10 11 |
jQuery.each( ("blur focus focusin focusout load resize scroll unload click 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 ); }; }); |
jQuery的實現很簡單,將事件直接通過each方法加入jQuery.fn上,然後在內部根據引數的不同直接對於jQuery的on或者trigger方法進行呼叫。
2.bind方法bind方法用於為一個元素繫結一個事件處理程式先來看看jQuery中的實現吧
1 2 3 |
bind: function( types, data, fn ) { return this.on( types, null, data, fn ); } |
3. live方法live方法拿到前面來說。這是jQuery現在已經不支援的方法。對於1.4.3以上的版本,推薦採delgate方法來,而對於1.7以上的版本,則推薦採用on方法來進行替代。具體採用live方法有什麼弊端,jQuery的文件之中已經闡述的非常明白了,至於為什麼會出現這個問題,本著刻苦求知的精神……我還並沒有去看,附上翻譯後的文件吧
1 |
因為更高版本的jQuery提供了更好的方法,沒有.live()方法的缺點,所以.live()方法不再推薦使用。特別是,使用.live()出現的以下問題: |
1 2 3 4 5 6 7 8 9 10 11 |
在呼叫 .live() 方法之前,jQuery 會先獲取與指定的選擇器匹配的元素,這一點對於大型文件來說是很花費時間的。 不支援鏈式寫法。例如,$("a").find(".offsite, .external").live( ... ); 這樣的寫法是不合法的,並不能像期待的那樣起作用。 由於所有的 .live() 事件被新增到 document 元素上,所以在事件被處理之前,可能會通過最長最慢的那條路徑之後才能被觸發。 在移動 iOS (iPhone, iPad 和 iPod Touch) 上,對於大多數元素而言,click 事件不會冒泡到文件 body 上,並且如果不滿足如下情況之一,就不能和 .live() 方法一起使用: 使用原生的可被點選的元素,例如, a 或 button,因為這兩個元素可以冒泡到 document。 在 document.body 內的元素使用 .on() 或 .delegate() 進行繫結,因為移動 iOS 只有在 body 內才能進行冒泡。 需要 click 冒泡到元素上才能應用的 CSS 樣式 cursor:pointer (或者是父元素包含 document.documentElement)。但是依需注意的是,這樣會禁止元素上的複製/貼上功能,並且當點選元素時,會導致該元素被高亮顯示。 在事件處理中呼叫 event.stopPropagation() 來阻止事件處理被新增到 document 之後的節點中,是效率很低的。因為事件已經被傳播到 document 上。 .live() 方法與其它事件方法的相互影響是會令人感到驚訝的。例如,$(document).unbind("click") 會移除所有通過 .live() 新增的 click 事件! |
4. delegate方法delegate方法也就是我們之前所提到的事件委託了。因為基於live之前的很多問題,jQuery在live之後的版本中增加了delgate方法(如今已經被on所取代)來看看現在的delegate是如何實現的吧
1 2 3 |
delegate: function( selector, types, data, fn ) { return this.on( types, selector, data, fn ); } |
5. one方法
為元素新增事件。並且在元素上的事件只可以執行一次
具體實現
1 2 3 |
one: function( types, selector, data, fn ) { return this.on( types, selector, data, fn, 1 ); } |
6. on方法
on方法在這裡不打算對原始碼進行閱讀,因為可以看到,上面的所有方法都是對於on方法的呼叫。因此jQuery的事件系統,核心就在於on方法了。因此jQuery的on方法將在後面進行分析,而這裡,只是對於on介面本身的一些說明。
on方法提供了繫結事件所有的功能。其API是這樣的
1 |
.on( events [, selector ] [, data ], handler(eventObject) ) |
其實最後還有一個one引數,倘若傳入了1,那麼這個事件也就只執行一次,而實現的原理其實也就是使用off來解綁事件進行的操作了。
然後來看下別的引數分別是做些什麼的吧
1 2 3 4 5 6 7 8 9 10 11 12 |
events: 型別: String 一個或多個空格分隔的事件型別和可選的名稱空間,或僅僅是名稱空間,比如"click", "keydown.myPlugin", 或者 ".myPlugin"。 selector: 型別: String 一個選擇器字串,用於過濾出被選中的元素中能觸發事件的後代元素。如果選擇器是 null 或者忽略了該選擇器,那麼被選中的元素總是能觸發事件。 data: 型別: Anything 當一個事件被觸發時,要傳遞給事件處理函式的event.data。 handler(eventObject) 型別: Function() 事件被觸發時,執行的函式。若該函式只是要執行return false的話,那麼該引數位置可以直接簡寫成 false。 |
因此再來對於前面五種方法來進行統一的分析。
直接用事件名進行事件繫結的方法與bind的方法,直接對於selector引數賦值為了null,因此,對於採用這兩種方法進行的事件繫結,其實本身是沒有使用事件委託的,即是說並沒有冒泡的過程,因此,採用這種方法的繫結對效能會有一定的損耗。而delegate方法,傳入了selector引數,因此,相當於也就是直接用on來實現的事件委託。one方法之前也講了,所以不再多說。
總結一下這章所講的內容吧。本章對於事件繫結的幾種方法(除on外),都進行了簡單地講解。與其說是對於jQuery原始碼的分析,倒不如說其實就是個簡單地,對於jQuery使用的建議吧。jQuery繫結的核心還是落在on方法上,同時,jQuery的事件系統的思想可以說是極為經典的。在下一章,我們將對jQuery的事件系統進行更深入一些的分析(在我捉急的水平範圍之內)。第三章估計要難產了,我一定會努力做完這個系列的,也算是對個人前端學習的一點鞭策吧。