直接与委托 - jQuery .on()

Posted

技术标签:

【中文标题】直接与委托 - jQuery .on()【英文标题】:Direct vs. Delegated - jQuery .on() 【发布时间】:2011-12-27 23:56:11 【问题描述】:

我试图了解使用jQuery .on() method 的直接委托 事件处理程序之间的这种特殊区别。具体来说,本段最后一句:

当提供selector 时,事件处理程序被称为委托。当事件直接发生在绑定元素上时,不会调用处理程序,而只会调用与选择器匹配的后代(内部元素)。 jQuery 将事件从事件目标冒泡到附加处理程序的元素(即从最内到最外的元素),并为匹配选择器的路径上的任何元素运行处理程序。

“为任何元素运行处理程序”是什么意思?我做了一个test page 来试验这个概念。但以下两种构造都会导致相同的行为:

$("div#target span.green").on("click", function() 
   alert($(this).attr("class") + " is clicked");
);

或者,

$("div#target").on("click", "span.green", function() 
   alert($(this).attr("class") + " is clicked");
);

也许有人可以参考一个不同的例子来澄清这一点?谢谢。

【问题讨论】:

所有感兴趣的人:jsperf.com/jquery-fn-on-delegate-vs-direct @KevinWheeler 我在下面评论了你的小提琴但here,基本上它没有正确设置(你绑定到父元素,并且委托是为孩子准备的)。要回答您的问题,这意味着委托处理程序将匹配新添加的元素,而没有委托的处理程序则不会。委托的好处是连接到浏览器的事件更少,从而导致应用程序的内存消耗更少,但代价是它增加了处理点击的时间(最少)。如果您正在制作游戏,请不要委托。 您引用的“测试页”不起作用。 【参考方案1】:

案例 1(直接):

$("div#target span.green").on("click", function() ...);

== 嘿!我希望 div#target 中的每个 span.green 都听:当你被点击时,做 X。

案例 2(委托):

$("div#target").on("click", "span.green", function() ...);

== 嘿,div#target!当您的任何“span.green”子元素被点击时,对它们执行 X。

换句话说...

在案例 1 中,每个跨度都已单独给出说明。如果创建了新跨度,他们将不会听到指令并且不会响应点击。每个跨度对自己的事件直接负责

在情况2中,只有容器得到了指令;它负责通知代表其子元素的点击。捕捉事件的工作已委派。这也意味着该指令将针对将来创建的子元素执行。

【讨论】:

这是一个很好的解释,让我一直拒绝理解的问题变得清晰。谢谢! 那么为什么on() 允许两个参数,而这与使用click() 几乎相同? .on() 是一个通用 API,可以处理任何类型的事件,包括多个不同的事件(您可以在第一个字符串中放置多个事件名称。) .click() 只是一个简写对于第一种形式。 @newbie, @N3dst4 : e.target 将是点击事件的初始目标(如果span.green 有子节点,则可以是子节点)。在处理程序内部,您应该使用 this 引用。见this fiddle。 另一个关于委托主题的注释 - 它非常有用且非常有效。与将事件附加到每个匹配元素相比,您将通过委托使用更少的浏览器资源。我想我会提到它,以防人们需要更多理由来使用委托。【参考方案2】:

第一种方式,$("div#target span.green").on(),在代码执行时将点击处理程序直接绑定到与选择器匹配的跨度。这意味着如果稍后添加其他跨度(或更改它们的类以匹配),它们就会错过并且不会有点击处理程序。这也意味着如果您稍后从其中一个跨度中删除“绿色”类,它的点击处理程序将继续运行 - jQuery 不会跟踪处理程序的分配方式并检查选择器是否仍然匹配。

第二种方式,$("div#target").on(),将点击处理程序绑定到匹配的 div(同样,这是针对当时匹配的那些),但是当在 div 的某处发生点击时,处理程序函数将仅当单击不仅发生在 div 中,而且发生在与 .on(),“span.green”的第二个参数中的选择器匹配的子元素中时才运行。以这种方式创建这些子跨度无关紧要,单击它们仍将运行处理程序。

因此,对于没有动态添加或更改其内容的页面,您不会注意到这两种方法之间的区别。如果您要动态添加额外的子元素,则第二种语法意味着您不必担心为它们分配点击处理程序,因为您已经在父元素上完成了一次。

【讨论】:

【参考方案3】:

N3dst4的解释很完美。基于此,我们可以假设所有子元素都在 body 中,因此我们只需要使用这个:

$('body').on('click', '.element', function()
    alert('It works!')
);

它适用于直接或委托事件。

【讨论】:

jquery 建议不要使用 body,因为它比较慢,因为脚本必须搜索 body 中的所有子项,在大多数情况下应该很多。使用元素的中间父容器会更好(更快)。 Jquery 删除了与你展示的一样的 live 方法。这是性能较弱的,不应使用。 在现代浏览器中,来自.element$('body').on() 委托的行为应该与在回调中带有if (Event.target.className.matches(/\belement\b/)) 的原生document.body.addEventHandler() 完全相同。由于$.proxy 开销,它在 jquery 中可能会稍微慢一些,但不要引用我的话。【参考方案4】:

与 OP 无关,但帮助我解开与此功能混淆的概念是 绑定元素必须是所选元素的父元素

Bound 是指.on 的剩余部分。 Selected 指的是.on() 的第二个参数。

委托不像 .find() 那样工作,选择绑定元素的子集。选择器仅适用于严格的子元素。

$("span.green").on("click", ...

很不一样
$("span").on("click", ".green", ...

特别是,为了获得@N3dst4 所暗示的“未来创建的元素”的优势,绑定元素必须是永久父级。然后被选中的孩子可以来来去去。

编辑

委派.on 不起作用的原因清单

$('.bound').on('event', '.selected', some_function) 可能不起作用的棘手原因:

    绑定元素不是永久。它是在调用.on() 之后创建的 所选元素不是绑定元素的正确。是同一个元素。 所选元素通过调用.stopPropagation() 阻止了事件冒泡到绑定元素。

(省略不那么棘手的原因,例如拼写错误的选择器。)

【讨论】:

【参考方案5】:

我写了一篇比较直接事件和委托的帖子。我比较的是纯js,但是对于只封装它的jquery来说意义相同。

结论是委托事件处理是针对动态DOM结构的,当用户与页面交互时可以创建绑定元素(不需要再次绑定),直接事件处理是针对静态DOM元素,当我们知道结构不会改变时.

欲了解更多信息和完整比较 - http://maciejsikora.com/standard-events-vs-event-delegation/

使用总是委托的处理程序,我认为这是当前非常流行的方式不是正确的方式,许多程序员使用它是因为“应该使用它”,但事实是直接事件处理程序更适合某些情况以及选择使用哪种方法应该得到差异知识的支持。

【讨论】:

如果附加许多事件处理程序(例如,表格的每一行),使用单个委托处理程序到容器而不是附加许多直接处理程序通常会更好的性能。从事件处理程序计数本身就是一个分析指标这一基本事实可以看出这一点。【参考方案6】:

案例 3(委托):

$("div#target").delegate("span.green", "click", function() ...);

【讨论】:

以上是关于直接与委托 - jQuery .on()的主要内容,如果未能解决你的问题,请参考以下文章

直接事件与事件委托

jQuery事件委托

jQuery 的事件绑定和事件委托(事件代理)

jquery中用on事件委托的方式绑定事件

jQuery完整的事件委托(on())

jQuery:使用委托时链接 on()