咱們試圖綁定一些事件到DOM元素上的時候,我相信上面這4個方法是最經常使用的。而它們之間到底有什麼不一樣呢?在什麼場合下用什麼方法是最有效的呢?html
當咱們在開始的時候,有些知識是必須具有的:jquery
下圖僅僅是一個示例,這是一個在browser環境下的一棵模擬DOM樹,在下面的代碼中僅起到演示的做用:瀏覽器
咱們的頁面能夠理解爲一棵DOM樹,當咱們在葉子結點上作什麼事情的時候(如click一個a元素),若是咱們不人爲的設置 stopPropagation(Moder Browser), cancelBubble(IE),那麼它的全部父元素,祖宗元素都會受之影響,它們上面綁定的事件也會產生做用。看一個示例:ide
$('a').bind('click', function() { alert("That tickles!") });函數
當咱們在a 上面點擊的時候,首先會觸發它自己所綁定的click事件,而後會一路往上,觸發它的父元素,祖先元素上全部綁定的click事件,就像下圖演示的那樣。this
爲了對下面的代碼進行演示,添加一些HTML代碼:spa
<ul id="members" data-role="listview" data-filter="true">code
<!-- ... more list items ... -->orm
<li>htm
<a href="detail.html?id=10">
<h3>John Resig(jQuery的做者)</h3>
<p><strong>jQuery Core Lead</strong></p>
<p>Boston, United States</p>
</a>
</li>
<!-- ... more list items ... -->
</ul>
.bind()是最直接的綁定方法 ,會綁定事件類型和處理函數到DOM element上, 這個方法是存在最久的,並且也很好的解決了瀏覽器在事件處理中的兼容問題。可是這個方法有一些performance方面的問題,看下面的代碼:
/* The .bind() method attaches the event handler directly to the DOM
element in question ( "#members li a" ). The .click() method is
just a shorthand way to write the .bind() method. */
$( "#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()方法用到了事件委託的概念來處理事件的綁定。它和用.bind()來綁定事件是同樣的。.live()方法會綁定相應的事件到你所選 擇的元素的根元素上,便是document元素上。那麼全部經過冒泡上來的事件均可以用這個相同的handler來處理了。它的處理機制是這樣的,一旦事 件冒泡到document上,jQuery將會查找selector/event metadata,而後決定那個handler應該被調用。jquery 1.8.2的源碼:
if (delegateCount && !(event.button && event.type === "click")) {
for (cur = event.target; cur != this; cur = cur.parentNode || this) {
// Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764)
if (cur.disabled !== true || event.type !== "click") {
selMatch = {};
matches = [];
for (i = 0; i < delegateCount; i++) {
handleObj = handlers[i];
sel = handleObj.selector;
if (selMatch[sel] === undefined) {
selMatch[sel] = handleObj.needsContext ?
jQuery(sel, this).index(cur) >= 0 :
jQuery.find(sel, this, null, [cur]).length;
}
if (selMatch[sel]) {
matches.push(handleObj);
}
}
if (matches.length) {
handlerQueue.push({ elem: cur, matches: matches });
}
}
}
}
當handler在執行的時候,由於有冒泡的參與,確實會有一些延遲,可是綁定的時候是特別的快。
/* The .live() method attaches the event handler to the root level
document along with the associated selector and event information
( "#members li a" & "click" ) */
$( "#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問題;
.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少不少了,不過,你仍是得浪費時間來查找;
其實.bind(), .live(), .delegate()都是經過.on()來實現的,.unbind(), .die(), .undelegate(),也是同樣的都是經過.off()來實現的,這是1.8.2的源碼:
bind: function( types, data, fn ) {
return this.on( types, null, data, fn );
},
unbind: function( types, fn ) {
return this.off( types, null, fn );
},
live: function( types, data, fn ) {
jQuery( this.context ).on( types, this.selector, data, fn );
return this;
},
die: function( types, fn ) {
jQuery( this.context ).off( types, this.selector || "**", fn );
return this;
},
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 );
},
看一下,咱們用如何用.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();
也許會對你產生一些困擾,由於它隱藏了一前面咱們所介紹的三種方法的細節;
用.bind()的代價是很是大的,它會把相同的一個事件處理程序hook到全部匹配的DOM元素上;
不要再用.live()了,它已經再也不被推薦了,並且還有許多問題;
.delegate()會提供很好的方法來提升效率,同時咱們能夠添加一事件處理方法到動態添加的元素上;
咱們能夠用.on()來代替上述的3種方法;