標籤:style blog http color 使用 os strong io
我們試圖綁定一些事件到DOM元素上的時候,我相信上面這4個方法是最常用的。而它們之間到底有什麼不同呢?在什麼場合下用什麼方法是最有效呢?
1.準備知識
當我們在開始的時候,有些知識是必須具備的:
1).DOM樹
僅僅是一個樣本,這是一個在browser環境下的一棵類比DOM樹,在下面的代碼中僅起到示範的作用:
2).Event bubbling (aka event propagation)冒泡
我們的頁面可以理解為一棵DOM樹,當我們在葉子結點上做什麼事情的時候(如click一個a元素),如果我們不人為的設定stopPropagation(Moder Browser), cancelBubble(IE),那麼它的所有父元素,祖宗元素都會受之影響,它們上面綁定的事件也會產生作用。看一個樣本:
複製內容到剪貼簿程式碼$(‘a‘).bind(‘click‘, function() { alert("That tickles!") });
當我們在a 上面點擊的時候,首先會觸發它本身所綁定的click事件,然後會一路往上,觸發它的父元素,祖先元素上所有綁定的click事件,就像示範的那樣。
3).樣本HTML
為了對下面的代碼進行示範,添加一些HTML代碼:
複製內容到剪貼簿程式碼
1 <ul id="members" data-role="listview" data-filter="true"> 2 <!-- ... more list items ... --> 3 <li> 4 <a href="detail.html?id=10"> 5 <h3>John Resig(jQuery的作者)</h3> 6 <p><strong>jQuery Core Lead</strong></p> 7 <p>Boston, United States</p> 8 </a> 9 </li>10 <!-- ... more list items ... -->11 </ul>
2.bind()
.bind()是最直接的Binder 方法 ,會綁定事件類型和處理函數到DOM element上, 這個方法是存在最久的,而且也很好的解決了瀏覽器在事件處理中的相容問題。但是這個方法有一些performance方面的問題,看下面的代碼:
複製內容到剪貼簿程式碼
1 /* The .bind() method attaches the event handler directly to the DOM 2 element in question ( "#members li a" ). The .click() method is 3 just a shorthand way to write the .bind() method. */4 5 $( "#members li a" ).bind( "click", function( e ) {} ); 6 $( "#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(),所以可能產生效率問題;
3.live()
.live()方法用到了事件委託的概念來處理事件的綁定。它和用.bind()來綁定事件是一樣的。.live()方法會綁定相應的事件到你所選擇的元素的根項目上,即是document元素上。那麼所有通過冒泡上來的事件都可以用這個相同的handler來處理了。它的處理機制是這樣的,一旦事件冒泡到document上,jQuery將會尋找selector/event metadata,然後決定那個handler應該被調用。jquery 1.8.2的源碼:
複製內容到剪貼簿程式碼
1 if (delegateCount && !(event.button && event.type === "click")) { 2 3 for (cur = event.target; cur != this; cur = cur.parentNode || this) { 4 5 // Don‘t process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764) 6 if (cur.disabled !== true || event.type !== "click") { 7 selMatch = {}; 8 matches = []; 9 for (i = 0; i < delegateCount; i++) {10 handleObj = handlers[i];11 sel = handleObj.selector;12 13 if (selMatch[sel] === undefined) {14 selMatch[sel] = handleObj.needsContext ?15 jQuery(sel, this).index(cur) >= 0 :16 jQuery.find(sel, this, null, [cur]).length;17 }18 if (selMatch[sel]) {19 matches.push(handleObj);20 }21 }22 if (matches.length) {23 handlerQueue.push({ elem: cur, matches: matches });24 }25 }26 }27 }
當handler在執行的時候,因為有冒泡的參與,確實會有一些延遲,但是綁定的時候是特別的快。
複製內容到剪貼簿程式碼
1 /* The .live() method attaches the event handler to the root level 2 document along with the associated selector and event information 3 ( "#members li a" & "click" ) */ 4 5 $( "#members li a" ).live( "click", function( e ) {} );
上面的code在和.bind()相比的時候有一個好處就是我們不需要在每個元素上再去綁定事件,而只在document上綁定一次就可以了。儘管這個不是最快的方式,但是確實是最少浪費的。
優點:
·這裡僅有一次的事件綁定,綁定到document上而不像.bind()那樣給所有的元素挨個綁定;
·那些動態添加的elemtns依然可以觸發那些早先綁定的事件,因為事件真正的綁定是在document上;
·你可以在document ready之前就可以綁定那些需要的事件;
缺點:
·從1.7開始已經不被推薦了,所以你也要開始逐步淘汰它了;
·Chaining沒有被正確的支援;
·當使用event.stopPropagation()是沒用的,因為都要到達document;
·因為所有的selector/event都被綁定到document, 所以當我們使用matchSelector方法來選出那個事件被調用時,會非常慢;
·當發生事件的元素在你的DOM樹中很深的時候,會有performance問題;
4.delegate()
.delegate()有點像.live(),不同於.live()的地方在於,它不會把所有的event全部綁定到document,而是由你決定把它放在哪兒。而和.live()相同的地方在雩都是用event delegation.
複製內容到剪貼簿程式碼
/* The .delegate() method behaves in a similar fashion to the .live() method, but instead of attaching the event handler to the document, you can choose where it is anchored ( "#members" ). The selector and event information ( "li a" & "click" ) will be attached to the "#members" element. */$( "#members" ).delegate( "li a", "click", function( e ) {} );
優點:
·你可以選擇你把這個事件放到那個元素上了;
·chaining被正確的支援了;
·jQuery仍然需要迭代尋找所有的selector/event data來決定那個子項目來匹配,但是因為你可以決定放在那個根項目上,所以可以有效減小你所要尋找的元素;
·可以用在動態添加的元素上;
缺點:
·需要尋找那個那個元素上發生了那個事件了,儘管比document少很多了,不過,你還是得浪費時間來尋找;
5.on()
其實.bind(), .live(), .delegate()都是通過.on()來實現的,.unbind(), .die(), .undelegate(),也是一樣的都是通過.off()來實現的,這是1.8.2的源碼:
複製內容到剪貼簿程式碼
1 bind: function( types, data, fn ) { 2 return this.on( types, null, data, fn ); 3 }, 4 unbind: function( types, fn ) { 5 return this.off( types, null, fn ); 6 }, 7 8 live: function( types, data, fn ) { 9 jQuery( this.context ).on( types, this.selector, data, fn );10 return this;11 },12 die: function( types, fn ) {13 jQuery( this.context ).off( types, this.selector || "**", fn );14 return this;15 },16 17 delegate: function( selector, types, data, fn ) {18 return this.on( types, selector, data, fn );19 },20 undelegate: function( selector, types, fn ) {21 // ( namespace ) or ( selector, types [, fn] )22 return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );23 },
看一下,我們用如何用.on()來改寫前面通過 .bind(), .live(), .delegate()所註冊的事件:
複製內容到剪貼簿程式碼
/* The jQuery .bind(), .live(), and .delegate() methods are just one line pass throughs to the new jQuery 1.8.2 .on() method */// Bind$( "#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();
缺點:
·也許會對你產生一些困擾,因為它隱藏了一前面我們所介紹的三種方法的細節;
6.結論
·用.bind()的代價是非常大的,它會把相同的一個事件處理常式hook到所有匹配的DOM元素上;
·不要再用.live()了,它已經不再被推薦了,而且還有許多問題;
·.delegate()會提供很好的方法來提高效率,同時我們可以添加一事件處理方法到動態添加的元素上;
·我們可以用.on()來代替上述的3種方法;
參考資料
[1].http://www.elijahmanor.com/2012/02/differences-between-jquery-bind-vs-live.html
[2].http://www.alfajango.com/blog/the-difference-between-jquerys-bind-live-and-delegate/