jQuery绑定方法的区别 - .bind() / .live() / .delegate() / .on()

时间:2022-08-25 09:36:01

介绍

本文只对原文重点内容做出翻译,几乎涵盖所有原文内容,如仍有疑惑请参考原文,地址在文章最下方给出。

<ul id="members" data-role="listview" data-filter="true">
<!-- ... more list items ... -->
<li>
<a href="detail.html?id=10">
<h3>John Resig</h3>
<p><strong>jQuery Core Lead</strong></p>
<p>Boston, United States</p>
</a>
</li>
<!-- ... more list items ... -->
</ul>

bind方法

bind方法直接把事件类型与事件句柄注册到所有满足选择器条件的DOM元素中。该方法经过长期的考验,直接可靠,只是会有些性能问题。如下

/* 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 ) {} );

上面两种方式的效果一摸一样,所有的链接都会独立绑定到同一个事件控制句柄,比较浪费资源。

优势 - 跨浏览器、方便快捷、支持快捷方法(.click()、.hover()等)、事件触发时响应速度快。

劣势 - 每个元素绑定同一个句柄副本(正确做法是绑定同一个句柄的引用)、无法应对动态加入的元素、元素多的时候会有性能问题、影响页面加载速度。

live方法

live采用事件委托的概念,其调用方式与bind类似。它将事件句柄连同选择器和事件信息一起绑定到document,这样就可以让事件句柄作用于所有事件。当事件冒泡至document时,jQuery则会查看选择器和事件来决定是否调用句柄。这种方式会在用户操作时多多少少有些影响性能,但初始注册时却很快。

/* 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 ) {} );

上面的代码与bind比起来,只需要把事件句柄绑定到document上就可以了,无需对所有满足条件的DOM元素一一绑定。这样做不仅效率高,而且节省资源,但这并不意味着这种方案毫无瑕疵。

优势 - 与bind方式相比,只需要一个事件句柄实例;从bind升级到live改动很小,只需要替换方法名字即可;动态添加到DOM树中,并满足选择器条件的元素也能按预期设想的那样工作,因为实际绑定的时候document元素,并非真实的目标DOM元素;句柄绑定可以在document的ready事件前完成,充分利用加载时间。

劣势 - jQuery 1.7中已不推荐使用该方法;这个方法对链式操作控制的不好;选择器中选出的元素基本上算是作废了,因为事件句柄是注册到document的;event.stopPropagation()不再起作用,因为document已经全权代理所有事件;因为所有的选择器和事件信息都是绑定到document的,所以在事件发生时jQuery会使用matchesSelector从大量的信息中找出匹配项来执行对应的事件句柄;事件会一直传播到document,如何DOM层次很深的话会对性能产生影响。

delegate方法

delegate的实现思路与live类似,但并不要求必须绑定到document,开发人员可以自行决定绑定元素。

/* 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 ) {} );

delegate的功能非常很强悍。上面的代码将事件句柄与事件信息一起注册到无序列表("#members")。这要比live总是注册到document的做法高效得多。另外delegate还解决了许多其它问题。

优势 - 目标绑定元素可指定;选择器不会预先执行,只是用来注册到根元素;支持链式操作;jQuery仍旧便利注册内容来查找匹配的选择器和事件,只是数据量相比document来说已经大大减少;适用于动态添加的元素;如果绑定对象为document,那么绑定操作可以在document的ready事件前执行。

劣势 - bind到delegate的过渡并不平滑,因为毕竟方法签名有出入;jQuery会使用matchesSelector并根据根元素中保存的选择器和事件信息来判断调用哪个句柄,其中的存储信息数量不容小视,只是与live相比还算是小的。

on方法

我们所熟知的bind、live和delegate底层都是靠jQuery 1.7中的新方法on实现的;unbind、die和undelegate则是通过off实现的。具体细节请参见下面的代码:

// ... more code ...

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 ) {
return arguments.length == 1 ?
this.off( selector, "**" ) :
this.off( types, selector, fn );
},

// ... more code ...

on的使用方式如下:

/* The jQuery .bind(), .live(), and .delegate() methods are just one 
line pass throughs to the new jQuery 1.7 .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 ) {} );

on看起来像是一个拥有不同签名、经过重载的方法。

优势 - 为各种事件绑定方法提供了一致的解决方案;简化jQuery代码,无需通过bind、live和delegate进行二次调用;在能够保证拥有delegate方法所有优点的前提下,同时拥有bind方法所提供的好处。

劣势 - 稍微有点混乱,其行为取决于被调用的方式。

总结

如何在合适的场景使用合适的方法:

使用bind方法开销比较大,因为它会把同一个事件句柄附加到选择器筛选出来的每一个元素。
不要使用live方法因为官方已经把它定位为deprecated,而且使用时还有许多问题。
delegate物超所值,既能满足性能上的需求,又能自如应对动态添加的元素。
新的on方法算得上是一种语法糖,替代bind、live和delegate。
新方向是使用on方法。熟悉它的语法并开始尝试在jQuery 1.7+项目中使用。

参考文档:
http://www.elijahmanor.com/differences-between-jquery-bind-vs-live-vs-delegate-vs-on/
https://gist.github.com/elijahmanor/1749717