所有 jquery 事件都应该绑定到 $(document)吗?

这是怎么回事

当我第一次学习 jQuery 的时候,我通常会附加这样的事件:

$('.my-widget a').click(function() {
$(this).toggleClass('active');
});

在学习了更多关于选择器速度和事件委托的知识之后,我在几个地方读到“ jQuery 事件委托将使您的代码更快”所以我开始写这样的代码:

$('.my-widget').on('click','a',function() {
$(this).toggleClass('active');
});

这也是推荐的方法来复制被否定的行为。现场直播。这对我来说很重要,因为我的很多站点一直在动态添加/删除小部件。上述行为并不完全像。但是 live () ,因为只有元素添加到已经存在的容器’。My-widget’会得到这种行为。如果在代码运行之后动态添加另一个 html 块,那么这些元素将不会得到绑定到它们的事件。像这样:

setTimeout(function() {
$('body').append('<div class="my-widget"><a>Click does nothing</a></div>');
}, 1000);


我想达到的目标:

  1. Live ()//的旧行为意味着将事件附加到尚不存在的元素
  2. 的好处
  3. 绑定事件的最快性能
  4. 管理事件的简单方法

我现在附上所有这样的事件:

$(document).on('click.my-widget-namespace', '.my-widget a', function() {
$(this).toggleClass('active');
});

这似乎满足了我所有的目标。(是的,在 IE 中,由于某些原因,速度变慢了,不知道为什么?) 它之所以快,是因为只有一个事件绑定到一个单数元素,而辅助选择器仅在事件发生时计算(如果这里有错误,请纠正我)。名称空间非常棒,因为它可以更容易地切换事件侦听器。

我的解决方案/问题

因此,我开始认为 jQuery 事件应该始终绑定到 $(document)。
你有什么理由不想这么做吗?
这能被认为是最佳实践吗? 如果不能,为什么

如果你已经读完了整篇文章,谢谢你。我很感谢你的任何/所有的反馈/见解。

假设:

  1. 使用支持 .on()//至少1.7版本的 jQuery
  2. 您希望将事件添加到动态添加的内容中

阅读资料/例子:

  1. Http://24ways.org/2011/your-jquery-now-with-less-suck
  2. Http://brandonaaron.net/blog/2010/03/4/event-delegation-with-jquery
  3. Http://www.jasonbuckboyer.com/playground/speed/speed.html
  4. Http://api.jquery.com/on/
33703 次浏览

不-您不应该将所有委托的事件处理程序绑定到 document对象。这可能是您能创建的最糟糕的情况。

首先,事件委托并不总是使您的代码更快。在某些情况下,它是有利的,而在某些情况下则不是。当您实际上需要事件委托并从中受益时,应该使用事件委托。否则,您应该将事件处理程序直接绑定到发生事件的对象,因为这样通常会更有效率。

其次,您不应该在文档级别绑定所有委托的事件。这正是 .live()被弃用的原因,因为当您以这种方式绑定大量事件时,这样做效率非常低。对于委托的事件处理,将它们绑定到非动态的最近的父节点要有效得多。

第三,并不是所有的事情都可以通过授权来解决,或者所有的问题都可以通过授权来解决。例如,如果您想拦截输入控件上的关键事件并阻止无效的键被输入到输入控件中,您不能使用委托事件处理来做到这一点,因为当事件冒泡到委托处理程序时,它已经被输入控件处理过了,现在要影响这一行为已经太晚了。

以下是需要或有利于活动授权的时间:

  • 当要捕获事件的对象被动态创建/删除时,仍然希望捕获事件,而不必在每次创建新事件处理程序时显式重新绑定事件处理程序。
  • 当你有很多对象都想要完全相同的事件处理程序(其中很多是至少数百)。在这种情况下,在设置时绑定一个委托的事件处理程序可能比绑定数百个或更多个直接事件处理程序更有效。注意,委托事件处理在运行时的效率总是低于直接事件处理程序。
  • 当您试图捕获(在文档的更高层次上)发生在文档中任何元素上的事件时。
  • 当您的设计显式地使用事件冒泡和停止传播()来解决页面中的某些问题或特性时。

为了更好地理解这一点,我们需要了解 jQuery 委托的事件处理程序是如何工作的。当你这样称呼:

$("#myParent").on('click', 'button.actionButton', myFn);

它在 #myParent对象上安装一个通用的 jQuery 事件处理程序。当单击事件冒泡到这个委托事件处理程序时,jQuery 必须遍历附加到该对象的委托事件处理程序列表,并查看事件的原始元素是否与委托事件处理程序中的任何选择器匹配。

因为选择器可能相当复杂,这意味着 jQuery 必须解析每个选择器,然后将其与原始事件目标的特征进行比较,以查看它是否与每个选择器匹配。这可不便宜。如果只有一个选择器,这没什么大不了的,但是如果你把所有的选择器放在文档对象上,并且有数百个选择器可以与每个冒泡事件进行比较,这会严重影响事件处理的性能。

因此,需要设置委托的事件处理程序,以便委托的事件处理程序尽可能接近目标对象。这意味着通过每个委托的事件处理程序冒泡的事件将更少,从而提高性能。将所有委托事件放在文档对象上是可能出现的性能最差的情况,因为所有冒泡事件都必须通过所有委托事件处理程序,并根据所有可能的委托事件选择器进行评估。这正是为什么 .live()被弃用的原因,因为 .live()就是这样做的,而且它被证明是非常低效的。


因此,为了实现最优化的性能:

  1. 只有在委托事件处理实际提供所需特性或提高性能时才使用委托事件处理。不要总是因为它很简单而使用它,因为你实际上并不需要它。在事件分派时,它实际上比直接事件绑定执行得更差。
  2. 将委托的事件处理程序尽可能地附加到距离事件源最近的父级。如果您使用委托事件处理是因为您希望为其捕获事件的动态元素,那么选择本身不是动态的最近的父元素。
  3. 对委托的事件处理程序使用易于计算的选择器。如果你了解委托事件处理是如何工作的,你就会明白,一个委托事件处理必须与许多对象进行多次比较,因此尽可能选择一个有效的选择器,或者为你的对象添加简单的类,这样可以使用更简单的选择器来提高委托事件处理的性能。

事件委托是一种在 DOM 中实际存在元素之前编写处理程序的技术。此方法有其自身的缺点,只有在您有这样的需求时才应该使用。

什么时候应该使用事件委托?

  1. 当您为需要相同功能的更多元素绑定通用处理程序时
    • 在本例中,如果必须使用直接绑定绑定所有行,那么最终将为该表中的 n 行创建 n 个处理程序。通过使用委托方法,您最终可以处理1个简单处理程序中的所有这些问题。
  2. 当您在 DOM 中更频繁地添加动态内容时(例如: 从表中添加/删除行)

为什么不应该使用事件委托?

  1. 与直接将事件绑定到元素相比,事件委托更慢。
    • 它比较每个泡沫的目标选择器,比较将是昂贵的,因为它是复杂的。
  2. 在触及绑定到的元素之前,无法控制事件冒泡。

PS: 即使对于动态内容,如果在内容插入到 DOM 之后绑定处理程序,也不必使用事件委托方法。(如添加的动态内容不经常删除/重新添加)

显然,事件代表团实际上是建议现在。至少对于香草 js。

Https://gomakethings.com/why-event-delegation-is-a-better-way-to-listen-for-events-in-vanilla-js/

”网上表演 # 感觉好像听到文档中的每一次点击都会影响性能,但实际上,这比在单个项目上安排一大堆事件监听器更有效。”

我想对 jfriend 00的回答添加一些评论和反驳(主要是基于我的直觉的观点)

不-您不应该将所有委托的事件处理程序绑定到文档 这可能是最糟糕的情况 创造。

首先,事件委托并不总是使您的代码更快 在某些情况下,它是有利的,而在某些情况下则不是 当您实际需要事件授权时,以及当您 否则,您应该将事件处理程序直接绑定到 事件发生的对象,因为这通常会更多 有效率。

虽然如果只为单个元素注册和事件,性能可能会稍微好一些,但我相信这并不能与委托带来的可伸缩性好处相提并论。我也相信浏览器将会越来越有效地处理这个问题,尽管我没有证据。在我看来,活动代表团才是正确的选择!

其次,您不应该绑定文档中的所有委托事件 这正是. live ()被弃用的原因,因为这非常 当你有很多事件以这种方式绑定时,效率会很低 事件处理将它们绑定到最接近的 非动态的父母。

我有点同意。如果您100% 确定事件只会发生在容器内,那么将事件绑定到这个容器是有意义的,但是我仍然反对将事件直接绑定到触发元素。

第三,不是所有的事件都可以工作,所有的问题都可以用它来解决 例如,如果希望拦截 输入控制和阻止无效键被输入到输入 控件,则不能对委托的事件处理进行此操作,因为 当事件冒泡到委托的处理程序时,它已经 已经被输入控制器处理过了,现在想影响已经太晚了 那种行为。

这是不正确的。请参阅这个代码笔: https://codepen.io/pwkip/pen/jObGmjq

document.addEventListener('keypress', (e) => {
e.preventDefault();
});

它演示了如何通过在文档上注册 keypress 事件来防止用户键入。

以下是需要或有利于活动授权的时间:

当要捕获事件的对象是动态的时候 创建/删除,并且仍然希望在不使用 每次创建新的 当你有很多对象都想要同样的事件 处理器(其中批次至少是数百)。在这种情况下,它可能是 在设置时更有效地绑定一个委托的事件处理程序 而不是数百或更多的直接事件处理程序 事件处理在运行时的效率总是低于直接事件 处理程序

我想以 https://ehsangazar.com/optimizing-javascript-event-listeners-for-performance-e28406ad406c的报价回复

Event delegation promotes binding as few DOM event handlers as possible, since each event handler requires memory. For example, let’s say that we have an HTML unordered list we want to bind event handlers to. Instead of binding a click event handler for each list item (which may be hundreds for all we know), we bind one click handler to the parent unordered list itself.

此外,搜索 performance cost of event delegation google会返回更多有利于事件委托的结果。

当您试图捕获(在文档的更高级别上) 在文档中的任何元素上发生的事件 显式地使用事件冒泡和停止传播()来解决一些 问题或特性。要了解这一点,一 需要了解 jQuery 委托的事件处理程序是如何工作的 这样称呼:

$(“ # myParent”) . on (‘ click’,‘ button.actionButton’,myFn) ; 在 # myParent 对象上的通用 jQuery 事件处理程序 事件冒泡到这个委托的事件处理程序,jQuery 必须关闭 通过附加到此对象的委托事件处理程序列表 并查看事件的原始元素是否与 委托事件处理程序中的选择器。

因为选择器可以相当复杂,这意味着 jQuery 具有 解析每个选择器,然后将其与 原始事件目标,以查看它是否匹配每个选择器 不是便宜的手术,只有一个也没什么大不了的, 但是如果你把所有的选择符放在 document 对象上 成百上千的选择器与每一个泡沫事件进行比较, 这会严重影响事件处理的性能。

出于这个原因,您希望设置委派的事件处理程序,以便 委托事件处理程序尽可能接近目标对象。 这意味着通过每个委托事件冒泡的事件将更少 处理程序,从而提高性能 在文档对象上是最差的性能,因为所有 冒泡事件必须通过所有委托的事件处理程序,并获取 根据所有可能的委托事件选择器计算 究竟为什么. live ()被弃用,因为这就是. live ()所做的和 结果证明效率很低。

这是在哪里记录的?如果这是真的,那么 jQuery 处理委托的效率似乎非常低,那么我的反对意见应该只应用于普通的 JS。

不过: 我希望能找到一个官方来源来支持这一说法。

歌曲: EDIT:

看起来 jQuery 确实在以一种非常低效的方式做事件冒泡(因为它们支持 IE8) Https://api.jquery.com/on/#event-performance

因此,我在这里的大多数论点只适用于普通的 JS 和现代浏览器。