jQuery中`click`、`bind`、`live`、`delegate`、`trigger`和`on`函数有什么区别?(附带示例)

140
我已阅读jQuery官方网站上每个函数的文档,但是以下函数之间没有这样的比较列表:
$().click(fn)
$().bind('click',fn)
$().live('click',fn)
$().delegate(selector, 'click', fn)
$().trigger('click') // UPDATED
$().on('click', selector ,fn); // more UPDATED

请避免任何参考链接。

所有以上函数的工作原理是什么,以及在什么情况下应该使用哪个?

注意: 如果有任何其他具有相同功能或机制的函数,请详细说明。

更新

我也看到了$.trigger函数。它是否与以上函数类似?

更多更新

现在在v1.7中添加了.on,我认为这个函数可以同时覆盖以上所有函数的要求。


3
有时候,如果问题可以通过查手册或者看起来像是作业问题,人们会投反对票。不用担心,如果有人发现它有用,他们会投赞成票的。 - typeoneerror
@Typeoneerror--感谢您的支持,我已经阅读了手册,但当我没有理解清楚区别时,我才在这里发帖。 - xkeshav
3
@I Like PHP - 对问题进行了一些清理编辑......这种问题经常被问到,但很少以这种方式进行,它很容易成为有价值的可谷歌搜索资源。我认为像这样的维基条目会非常有帮助,我每天都看到关于.live().delegate()的困惑,对一个完全有效的问题表示赞同。 - Nick Craver
@Nick Craver 感谢您的编辑,实际上我英语很烂(哈哈)。有没有什么手册/参考资料告诉我们如何在 SO 上发问题。或者这只能通过经验来获得? - xkeshav
@我喜欢PHP - 它有点两者兼备,这里有一个很好的SO常见问题解答:http://meta.stackexchange.com/questions/7931 对于措辞/评论,你只需要开始学习什么最合适,以及传达你的想法的最佳方式,代码>任何描述有时候,使用正确的关键字,适当地标记,只是随着时间的推移而来,我看到很多海报随着时间的推移而改进。至于你的编辑,.trigger()只是调用事件处理程序...我会在下面的答案中添加描述。 - Nick Craver
一个有用的链接:http://www.elijahmanor.com/2012/02/differences-between-jquery-bind-vs-live.html - xkeshav
5个回答

163

在阅读本文之前,请在另一页中查看事件列表,API本身非常有帮助,我下面讨论的所有内容都直接链接到此页面

首先,.click(function).bind('click', function) 的缩写,它们是等价的。当直接将处理程序绑定到元素时,请使用它们,如下所示:

$(document).click(function() {
  alert("You clicked somewhere in the page, it bubbled to document");
});

如果该元素被替换或丢弃,该处理程序将不再存在。此外,当附加处理程序时(例如,选择器找到它时),不存在的元素也不会获得处理程序。 .live().delegate() 类似相关,.delegate() 实际上在内部使用 .live(),它们都监听事件冒泡。这适用于新旧元素,它们以相同的方式冒泡事件。当元素可能会更改时,例如添加新行、列表项等时,您可以使用这些内容。如果没有一个父/共同祖先将留在页面并且在任何时候都不会被替换,请使用 .live(),如下所示:
$(".clickAlert").live('click', function() {
  alert("A click happened");
});

如果您确实有一个父元素没有被替换(因此其事件处理程序不会消失),则应使用.delegate()来处理它,如下所示:

$("#commonParent").delegate('.clickAlert', 'click', function() {
  alert("A click happened, it was captured at #commonParent and this alert ran");
});

这个方法与.live()几乎相同,但事件在被捕获并执行处理程序之前冒泡的次数更少。另一个常见用途是当元素的类更改时,不再匹配最初使用的选择器...使用这些方法,选择器在事件发生时进行评估,如果匹配,则运行处理程序...因此,不再匹配选择器的元素无关紧要,它将不再执行。然而,使用.click()绑定事件处理程序直接在DOM元素上,它不匹配任何查找它的选择器是无关紧要的...事件被绑定并且会一直保留,直到该元素消失或通过.unbind()移除处理程序。 .live().delegate()的另一个常见用途是提高性能。如果您正在处理大量元素,则直接向每个元素附加单击处理程序是昂贵且耗时的。在这些情况下,更经济的方法是设置一个单一的处理程序,并让冒泡完成工作,这是一个应用的很好例子,请参阅take a look at this question where it made a huge difference

触发事件 - 针对更新的问题

有两个主要的事件处理程序触发函数可用,它们都属于API中相同的“事件处理程序附加”类别, 它们是 .trigger().triggerHandler().trigger('eventName') 内置了一些常见事件的快捷方式,例如:

$().click(fn); //binds an event handler to the click event
$().click();   //fires all click event handlers for this element, in order bound

您可以在此处查看包括这些快捷方式的列表

至于区别,.trigger() 触发事件处理程序(但大多数情况下不触发默认操作,例如将光标放置在点击的 <textarea> 中的正确位置)。它会按照绑定的顺序引起事件处理程序(就像本地事件一样),启动本地事件行动,并冒泡到 DOM。

.triggerHandler() 通常用于不同的目的,这里你只是想要激活绑定的处理程序,它不会触发本地事件,例如提交表单。它不会冒泡到 DOM,也不可链式调用(它返回该事件的最后一个绑定事件处理程序返回的任何内容)。例如,如果您想触发一个 focus 事件,但实际上不想聚焦该对象,只需要运行您使用 .focus(fn) 绑定的代码,这将完成此操作,而 .trigger() 将聚焦该元素并冒泡。

这是一个现实世界的例子:

$("form").submit(); //actually calling `.trigger('submit');`

这将运行任何提交处理程序,例如jQuery验证插件,然后尝试提交<form>。但是,如果您只想验证,因为它通过submit事件处理程序连接,但之后不提交<form>,您可以像这样使用.triggerHandler('submit')
$("form").triggerHandler('submit');

该插件通过在验证检查未通过时终止处理程序来防止提交表单,但使用此方法,我们不关心它的操作。无论它是否中止,我们都不会尝试提交表单,我们只想触发重新验证并且不执行其他操作。(免责声明:由于插件中有一个.validate()方法,因此这是一个多余的示例,但它是意图的一个很好的说明)。

非常详细。一个小修正:trigger 不会触发原生事件。所谓的原生事件是指使用 fireEvent(IE)或 dispatchEvent(w3c)模拟的事件。 - Crescent Fresh
@Crescent - 我更新了一下,让表述更加清晰明了。我的意思是它会触发本地 actions 事件,比如表单提交、链接跟踪等等... 希望更新后更加明确 :) - Nick Craver
@Nick,从我所阅读的内容来看,您对live()的理解似乎与您在这里给我的回答不同:https://dev59.com/cFHTa4cB1Zd3GeqPQEm7#3983979 。在使用live()时,“权重”仍然是一个问题吗? - Yahel
@yahelc - 当然可以...这个答案严格比较了事件处理程序的选项及其特定目的。重量成本与初始绑定数量,是否动态添加任何内容以及初始选择器仍然是有效的问题。如果您的页面结构不是那么大,您没有那么多的事件,那么您就没问题了...对于重量的担忧与您的结构大小/深度和正在生成的事件数量(您的.live()调用监听的类型,例如click)直接成正比,因为它会为每个事件运行选择器。 - Nick Craver
1
在jQuery 1.7中,live()已被弃用,推荐使用$(document).on()。 - Synchro
虽然我理解了live/on和bind/click之间的区别,但在后者中,只有绑定是固定的还是执行也是固定的,例如: $('#mybutton').click(myfunction($('myselect).value); 当单击时,它会传递$('myselect)的值还是在绑定时传递? - Synchro

28

前两个是等价的。

// The following two statements do the same thing:
$("blah").click( function() { alert( "Click!" ); } );
$("blah").bind( "click", function() { alert( "Click!" ); } ); 

使用第二种方法,可以通过指定多个以空格分隔的事件名称来绑定多个事件:

$("blah").bind( "click mouseover mouseout", function() { alert( "Click! Or maybe mouse moved." ); } ); 

.live方法更有趣。考虑以下示例:

<a class="myLink">A link!</a>
<a id="another">Another link!</a>

<script>
    $("a.myLink").click( function() { alert( 'Click!' ); } );

    $("a#another").addClass( "myLink" );
</script>
在脚本的第二行执行后,第二个链接也将拥有“myLink”的CSS类。但是它将没有事件处理程序,因为在附加事件时它没有该类。
现在考虑你希望反过来:每当页面上出现一个具有“myLink”类的链接时,您都希望自动具有相同的事件处理程序。当您具有某种列表或表格且动态添加行或单元格但希望它们以相同的方式运作时,这是非常常见的。您可以使用`.live`方法而不必每次分配事件处理程序来避免所有痛苦:
<a class="myLink">A link!</a>
<a id="another">Another link!</a>

<script>
    $("a.myLink").live( "click", function() { alert( 'Click!' ); } );

    $("a#another").addClass( "myLink" );
</script>

在这个例子中,第二个链接获取"myLink" class后也会立即获得事件处理程序。太神奇了! :-)

当然,它并不是那么字面上的。实际上,.live所做的是将处理程序附加到HTML树的根部(即"body"元素),而不是指定的元素本身。在DHTML中,事件具有"冒泡"的有趣特性。考虑一下这个例子:

<div> <a> <b>text</b> </a> </div>

如果您点击"text",则首先会触发<b>元素的"click"事件。然后,<a>元素将获得"click"事件。之后,<div>元素将获得"click"事件。一直往上,一直到<body>元素。这就是jQuery捕捉事件的位置,并查看是否有适用于首次引起事件的元素的任何"live"处理程序。很妙!

最后,是.delegate方法。它只需获取符合给定选择器的所有子元素并为它们附加一个"live"处理程序。看下面的例子:

$("table").delegate( "td", "click", function() { alert( "Click!" ); } );

// Is equivalent to:
$("table").each( function() {
    $(this).find( "td" ).live( "click", function() { alert( "Click!" ); } );
} );

有问题吗?


准确来说,.live() 绑定的是 document 而不是 <body> :) 你可以在这里查看演示,在控制台中检查:http://jsfiddle.net/aJy2B/ - Nick Craver
3
这样解释会更方便。 :-) - Fyodor Soikin
4
关于“body”部分的说法是可以接受的,但是“一直到<body>元素”的说法是错误的,事件会继续向上冒泡,.live()处理程序不在那里,它们位于上面的document :) 您可以在此处查看演示:http://jsfiddle.net/S2VBX/。 - Nick Craver

8
自jQuery 1.7版本起,.live()方法已被弃用。如果您使用的是jQuery版本< 1.7,则官方建议使用.delegate()代替.live()。
.live()现在已被.on()取代。
最好直接访问jQuery网站以获取更多信息,但以下是当前版本的.on()方法:
.on( events [, selector] [, data], handler(eventObject) )
.on( events-map [, selector] [, data] )

http://api.jquery.com/on/


2
$().click(fn)$().bind('click', fn)在表面上看起来是相同的,但$.bind版本有两个更强大的原因:
  1. $().bind()允许您为多个事件分配一个处理程序,例如$().bind('click keyup', fn)
  2. $().bind()支持命名空间事件-如果您想仅删除(解除绑定)元素绑定的某些事件处理程序,则这是一项强大的功能-请阅读更多信息:命名空间事件

现场对代表:这已经在其他回复中回答过了。


1

这就是阅读API可能会有所帮助的地方。然而,我知道这个问题的答案,所以你可以继续偷懒(耶!)。

$('#something').click(fn);
$('#something').bind('click',fn);

在我所知道的范围内,这里没有任何区别。 .click 只是一个方便/辅助方法,用于绑定 .bind('click'

// even after this is called, all <a>s in
// <div class="dynamic_els"> will continue
// to be assigned these event handlers

$('div.dynamic_els a').live(‘click’,fn);

这是非常不同的,因为 .live 会将事件添加到您传递的选择器中(但您在此处没有传递)并继续查看 DOM,因为节点被插入/删除

$('#some_element').delegate('td','click',fn);

这个方法的不同之处在于你分配事件处理程序的方式。 .delegate 是基于 DOM 事件冒泡的。 基本原则是每个事件都会向上冒泡到 DOM 树,直到达到根元素(documentwindow<html><body>,我记不清了)。

无论如何,你都要将一个 onclick 处理程序绑定到 $('#some_element') 中的所有 <td> 上(你必须指定一个选择器,虽然你可以说 $(document))。 当其中一个子元素被点击时,事件会冒泡到 <td>。 然后,你可以提取事件的源元素(jQuery 会自动为你完成)。

当有大量元素且只有少数(或一个中心)点通过这些事件时,这非常有用。 这样可以节省浏览器的工作和内存,将这些事件处理程序合并成较少的对象。


1
.live()也可以使用事件冒泡,实际上.delegate().live()的一个包装器,它只是添加了一个上下文和绑定到一个元素(而不是document)来捕获冒泡。我认为你对冒泡处理程序的理解有点偏差(这是jQuery 1.4中最常被误解的方面)。处理程序在您绑定它的元素上,因此无论您在哪个元素上调用了.delegate(),或者在.live()的情况下是document,当事件冒泡到那里时,它会检查目标以查看是否与选择器匹配,如果匹配,则执行。 - Nick Craver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接