1. 程式人生 > >jQuery四種事件繫結方式.bind(),.live(),.delegate(),on()的區別

jQuery四種事件繫結方式.bind(),.live(),.delegate(),on()的區別

.bind().live(), 和 .delegate()之間的區別並不明顯。但是理解它們的不同之處有助於寫出更簡潔的程式碼,並防止我們的互動程式中出現沒有預料到的bug。

基礎

DOM樹

首先,圖形化的HTML文件能幫助我們更好的理解。一個簡單的HTML頁面看起來應該像這樣

DOM tree sample

事件冒泡(也稱作事件傳遞)(Event bubbling aka event propagation)

點選一個連結,觸發繫結在連結元素上的 click 事件,進而觸發繫結到這個元素的click事件的函式。

$('a').bind('click',function() { alert("That tickles!"

) });

所以一次點選會觸發一個alert。
trigger the alert
然後,這個 click 事件會從DOM樹向上傳遞,傳播到父元素,然後傳遞給每一個祖先元素。
event propagates up the tree

在DOM樹中, document 是根節點。
現在我們能容易的解釋.bind().live(), 和 .delegate()之間的差別了

.bind()

$('a').bind('click',function() { alert("That tickles!") });

這是最直接的繫結方法。jQuery 掃描文件找到所有 $(‘a’) 元素,然後給每一個找到的元素的 click 事件繫結處理函式。

$( "#members li a" ).bind( "click", function
( e ) {} ); $( "#members li a" ).click( function( e ) {} );

上面的兩行程式碼所完成的任務都是一致的,就是把event handler加到全部的匹配的<a>元素上。這裡存在著一些效率方面的問題,一方面,我們隱式地把click handler加到所有的a標籤上,這個過程是昂貴的;另一方面在執行的時候也是一種浪費,因為它們都是做了同一件事卻被執行了一次又一次(比如我們可以把它hook到它們的父元素上,通過冒泡可以對它們中的每一個進行區分,然後再執行這個event handler)。

優點:

  • 這個方法提供了一種在各種瀏覽器之間對事件處理的相容性解決方案
  • 非常方便簡單的繫結事件到元素上
  • .click(), .hover()...這些非常方便的事件繫結,都是bind的一種簡化處理方式
  • 對於利用ID選出來的元素是非常好的,不僅僅是很快的可以hook上去(因為一個頁面只有一個id),而且當事件發生時,handler可以立即被執行(相對於後面的live, delegate)實現方式

缺點:

  • 它會繫結事件到所有的選出來的元素上
  • 它不會繫結到在它執行完後動態新增的那些元素上
  • 當元素很多時,會出現效率問題
  • 當頁面載入完的時候,你才可以進行bind(),所以可能產生效率問題

.live()

$('a').live('click',function() { alert("That tickles!") });  

  jQuery繫結處理函式到 $(document) 元素,並把 ‘click’ 和 ‘a’ 作為函式的引數。有事件冒泡到document節點的時候,檢查這個事件是不是 click 事件,target element能不能匹配 ‘a’ css選擇器,如果兩個條件都是true,處理函式執行。

live方法也可以繫結到指定的元素(或者說“上下文(context)”)而不用繫結到document,比如:

$('a',$('#container')[0]).live(...);

優點:

  • 這裡僅有一次的事件繫結,繫結到document上而不像.bind()那樣給所有的元素挨個繫結
  • 那些動態新增的elemtns依然可以觸發那些早先繫結的事件,因為事件真正的繫結是在document上
  • 你可以在document ready之前就可以繫結那些需要的事件

缺點:

  • 從1.7開始已經不被推薦了,所以你也要開始逐步淘汰它了。
  • Chaining沒有被正確的支援
  • 當使用event.stopPropagation()是沒用的,因為都要到達document
  • 因為所有的selector/event都被繫結到document, 所以當我們使用matchSelector方法來選出那個事件被呼叫時,會非常慢
  • 當發生事件的元素在你的DOM樹中很深的時候,會有performance問題

.delegate()

$('#container').delegate('a','click',function() { alert("That tickles!") });  

jQuery掃描文件找到 $(‘#container’),繫結處理函式到他的 click 事件,’a’ css選擇器作為函式的引數。當有事件冒泡到 $(‘#container’),檢查事件是不是 click,並檢查target element是不是匹配css選擇器,如果兩者都符合,執行函式。

注意這次和 .live() 方法很相似,除了把事件繫結到特定元素與跟元素的區別。精明的JS’er 或許會總結成 $(‘a’).live() == $(document).delegate(‘a’),真的是這樣嗎? 不,不全是。

優點:

  • 你可以選擇你把這個事件放到那個元素上了
  • chaining被正確的支援了
  • jQuery仍然需要迭代查詢所有的selector/event data來決定那個子元素來匹配,但是因為你可以決定放在那個根元素上,所以可以有效的減小你所要查詢的元素。
  • 可以用在動態新增的元素上

缺點:

  • 需要查詢那個那個元素上發生了那個事件了,儘管比document少很多了,不過,你還是得浪費時間來查詢。

為什麼 .delegate() 比 .live() 好

jQuery 的 delegate方法比 live 方法更應該成為首選有一個原因。考慮以下的場景:

$('a').live('click',function() { blah() }); // or $(document).delegate('a','click',function() { blah() });   

速度

上面第二個執行比第一個快,因為第一個會遍歷整個文件查詢 $(‘a’) 元素,並儲存為jQuery物件,但是live方法只需要傳一個字串引數’a'而已,$() 方法並不知道我們會用鏈式表示式在後面用上.live()。

delegate 方法就只需要找到並存貯 $(document)元素就夠了。

        有一種hack是在 $(document).ready()之外呼叫live方法,這樣就會立即執行。這時候DOM還沒有填充,也就不會查詢元素或建立jQuery物件。

靈活性和鏈式語法

這方面live方法依然令人費解。想一下,它鏈在$(‘a’)物件,但實際上是在$(document)物件起作用。因為這個原因,在鏈式表示式中使用live讓人很不安,我覺得live方法變成一個全域性的jQuery方法 $.live(‘a’,…) 會更有意義。

只支援css選擇器

最後,live方法有一個最大的缺點,只能用css選擇器,用起來很不方便。

為什麼使用 .live() 或 .delegate() 而不用 .bind()

最後,bind 方法看起來更清晰,更直接,是嗎?但是這裡有兩個原因我們推薦 delegate 或 live:

  • 繫結事件處理函式到還不存在DOM中的元素。 bind 方法直接繫結函式到每個單獨的元素,不能繫結到還沒有新增到頁面裡的元素,如果你寫了$(‘a’).bind(…),然後用ajax給頁面增加了新的連結,新新增的連結不會繫結事件。live 或 delegate 或者其它繫結到祖先元素的事件,讓現在有的元素,或者以後增的元素都可以使用。
  • 繫結處理函式到一個元素或者少數幾個元素,監聽後代元素,而不是繫結100個相同的處理函式到單獨的元素。這樣更有效能優勢。

.On()

 其實.bind(), .live(), .delegate()都是通過.on()來實現的,.unbind(), .die(), .undelegate(),也是一樣的都是通過.off()來實現的,

看一下,我們用如何用.on()來改寫前面通過 .bind(), .live(), .delegate()所註冊的事件:
$( "#members li a" ).on( "click", function( e ) {} ); 
$( "#members li a" ).bind( "click", function( e ) {} ); 

// Live
$( document ).on( "click", "#members li a", function( e ) {} ); 
$( "#members li a" ).live( "click", function( e ) {} );

// Delegate
$( "#members" ).on( "click", "li a", function( e ) {} ); 
$( "#members" ).delegate( "li a", "click", function( e ) {} );

優點:

  • 提供了一種統一繫結事件的方法
  • 仍然提供了.delegate()的優點,當然如果需要你也可以直接用.bind()

缺點:

  • 也許會對你產生一些困擾,因為它隱藏了一前面我們所介紹的三種方法的細節。

結論:

  • 用.bind()的代價是非常大的,它會把相同的一個事件處理程式hook到所有匹配的DOM元素上
  • 不要再用.live()了,它已經不再被推薦了,而且還有許多問題
  • .delegate()會提供很好的方法來提高效率,同時我們可以新增一事件處理方法到動態新增的元素上。
  • 我們可以用.on()來代替上述的3種方法

阻止冒泡

最後注意一下事件冒泡。通常我們能用這樣的方法阻止其他處理函式:

$('a').bind('click',function(){   e.preventDefault();    //or    e.stopPropagation(); })   

但是在這裡,用 live 或 delegate 方法繫結的事件會一直傳遞到事件真正繫結的地方才會執行。這時其他的函式已經執行過了。

阻止事件冒泡:

if (window.event) {
  e.cancelBubble=true;// ie6、7、8下阻止冒泡
 } else {
  //e.preventDefault();
  e.stopPropagation();// 其它標準瀏覽器下阻止冒泡
 }