http://blog.csdn.net/helloliuhai/article/details/19987509
.bind(), .live(), 和 .delegate()之间的区别并不明显。但是理解它们的不同之处有助于写出更简洁的代码,并防止我们的交互程序中出现没有预料到的bug。
基础
DOM树
首先,图形化的HTML文档能帮助我们更好的理解。一个简单的HTML页面看起来应该像这样
事件冒泡(也称作事件传递)(Event bubbling aka event propagation)
点击一个链接,触发绑定在链接元素上的 click 事件,进而触发绑定到这个元素的click事件的函数。
$(
'a'
).bind(
'click'
,
function
() { alert(
"That tickles!"
) });
所以一次点击会触发一个alert。
然后,这个 click 事件会从DOM树向上传递,传播到父元素,然后传递给每一个祖先元素。
在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();// 其它标准浏览器下阻止冒泡
}