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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12824549/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 08:34:57  来源:igfitidea点击:

Should all jquery events be bound to $(document)?

javascriptjqueryjquery-selectorsjquery-on

提问by Buck

Where this is coming from

这是从哪里来的

When I first learned jQuery, I normally attached events like this:

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

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

After learning more about selector speed and event delegation, I read in several places that "jQuery event delegation will make your code faster." So I started to write code like this:

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

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

This was also the recommended way to replicate the behavior of the deprecated .live() event. Which is important to me since a lot of my sites dynamically add/remove widgets all the time. The above doesn't behave exactly like .live() though, since only elements added to the already existing container '.my-widget' will get the behavior. If I dynamically add another block of html after that code has ran, those elements will not get the events bound to them. Like this:

这也是复制已弃用的 .live() 事件行为的推荐方法。这对我很重要,因为我的很多网站一直在动态添加/删除小部件。上面的行为并不完全像 .live() ,因为只有添加到现有容器 '.my-widget' 的元素才会获得行为。如果我在该代码运行后动态添加另一个 html 块,这些元素将不会将事件绑定到它们。像这样:

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



What I want to achieve:

我想要达到的目标:

  1. the old behavior of .live() // meaning attaching events to not yet existent elements
  2. the benefits of .on()
  3. fastest performance to bind events
  4. Simple way to manage events
  1. .live() 的旧行为 // 意味着将事件附加到尚不存在的元素
  2. .on() 的好处
  3. 绑定事件的最快性能
  4. 管理事件的简单方法

I now attach all events like this:

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

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

Which seems to meet all my goals. (Yes it's slower in IE for some reason, no idea why?) It's fast because only a single event is tied to a singular element and the secondary selector is only evaluated when the event occurs (please correct me if this is wrong here). The namespace is awesome since it makes it easier to toggle the event listener.

这似乎满足了我的所有目标。(是的,由于某种原因,它在 IE 中变慢了,不知道为什么?)它很快,因为只有一个事件与单个元素相关联,并且辅助选择器仅在事件发生时进行评估(如果这里有错误,请纠正我)。命名空间很棒,因为它可以更轻松地切换事件侦听器。

My Solution/Question

我的解决方案/问题

So I'm starting to think that jQuery events should always be bound to $(document).
Is there any reason why you would not want to do this?
Could this be considered a best practice? If not, why?

所以我开始认为 jQuery 事件应该总是绑定到 $(document)。
你有什么理由不想这样做吗?
这可以被视为最佳实践吗?如果不是,为什么?

If you've read this whole thing, thank you. I appreciate any/all feedback/insights.

如果您已阅读全文,谢谢。我感谢任何/所有反馈/见解。

Assumptions:

假设:

  1. Using jQuery that supports .on()// at least version 1.7
  2. You want the the event to be added to dynamically added content
  1. 使用支持.on()// 至少 1.7 版的jQuery
  2. 您希望将事件添加到动态添加的内容中

Readings/Examples:

读物/例子:

  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/
  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/

回答by jfriend00

No - you should NOT bind all delegated event handlers to the documentobject. That is probably the worst performing scenario you could create.

否 - 您不应将所有委托的事件处理程序绑定到该document对象。这可能是您可以创建的性能最差的场景。

First off, event delegation does not always make your code faster. In some cases, it's is advantageous and in some cases not. You should use event delegation when you actually need event delegation and when you benefit from it. Otherwise, you should bind event handlers directly to the objects where the event happens as this will generally be more efficient.

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

Second off, you should NOT bind all delegated events at the document level. This is exactly why .live()was deprecated because this is very inefficient when you have lots of events bound this way. For delegated event handling it is MUCH more efficient to bind them to the closest parent that is not dynamic.

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

Third off, not all events work or all problems can be solved with delegation. For example, if you want to intercept key events on an input control and block invalid keys from being entered into the input control, you cannot do that with delegated event handling because by the time the event bubbles up to the delegated handler, it has already been processed by the input control and it's too late to influence that behavior.

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

Here are times when event delegation is required or advantageous:

以下是需要或有利事件委托的时候:

  • When the objects you are capturing events on are dynamically created/removed and you still want to capture events on them without having to explicitly rebind event handlers every time you create a new one.
  • When you have lots of objects that all want the exact same event handler (where lots is at least hundreds). In this case, it may be more efficient at setup time to bind one delegated event handler rather than hundreds or more direct event handlers. Note, delegated event handling is always less efficient at run-time than direct event handlers.
  • When you're trying to capture (at a higher level in your document) events that occur on any element in the document.
  • When your design is explicitly using event bubbling and stopPropagation() to solve some problem or feature in your page.
  • 当您在其上捕获事件的对象是动态创建/删除的,并且您仍然希望在它们上捕获事件而不必在每次创建新事件处理程序时显式重新绑定事件处理程序。
  • 当您有很多对象都需要完全相同的事件处理程序时(其中很多至少是数百个)。在这种情况下,在设置时绑定一个委托的事件处理程序可能比数百个或更多的直接事件处理程序更有效。请注意,委托事件处理在运行时的效率总是低于直接事件处理程序。
  • 当您尝试捕获(在文档中的更高级别)发生在文档中任何元素上的事件时。
  • 当您的设计明确使用事件冒泡和 stopPropagation() 来解决页面中的某些问题或功能时。


To understand this a little more, one needs to understand how jQuery delegated event handlers work. When you call something like this:

要进一步理解这一点,需要了解 jQuery 委托的事件处理程序是如何工作的。当你调用这样的东西时:

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

It installs a generic jQuery event handler on the #myParentobject. When a click event bubbles up to this delegated event handler, jQuery has to go through the list of delegated event handlers attached to this object and see if the originating element for the event matches any of the selectors in the delegated event handlers.

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

Because selectors can be fairly involved, this means that jQuery has to parse each selector and then compare it to the characteristics of the original event target to see if it matches each selector. This is not a cheap operation. It's no big deal if there is only one of them, but if you put all your selectors on the document object and there were hundreds of selectors to compare to every single bubbled event, this can seriously start to hobble event handling performance.

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

For this reason, you want to set up your delegated event handlers so a delegated event handler is as close to the target object as practical. This means that fewer events will bubble through each delegated event handler, thus improving the performance. Putting all delegated events on the document object is the worst possible performance because all bubbled events have to go through all delegated event handlers and get evaluated against all possible delegated event selectors. This is exactly why .live()is deprecated because this is what .live()did and it proved to be very inefficient.

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



So, to achieve optimized performance:

因此,要实现优化的性能:

  1. Only use delegated event handling when it actually provides a feature you need or increases performance. Don't just always use it because it's easy because when you don't actually need it. It actually performs worse at event dispatch time than direct event binding.
  2. Attach delegated event handlers to the nearest parent to the source of the event as possible. If you are using delegated event handling because you have dynamic elements that you want to capture events for, then select the closest parent that is not itself dynamic.
  3. Use easy-to-evaluate selectors for delegated event handlers. If you followed how delegated event handling works, you will understand that a delegated event handler has to be compared to lots of objects lots of times so picking as efficient a selector as possible or adding simple classes to your objects so simpler selectors can be used will increase the performance of delegated event handling.
  1. 只有在实际提供您需要的功能或提高性能时才使用委托事件处理。不要总是使用它,因为它很容易,因为当您实际上不需要它时。实际上,它在事件调度时的性能比直接事件绑定更差。
  2. 尽可能将委托的事件处理程序附加到离事件源最近的父级。如果您使用委托事件处理是因为您有要为其捕获事件的动态元素,则选择最接近的本身不是动态的父级。
  3. 为委托的事件处理程序使用易于评估的选择器。如果您遵循委托事件处理的工作原理,您就会明白委托事件处理程序必须多次与大量对象进行比较,因此尽可能选择高效的选择器或向对象添加简单的类以便可以使用更简单的选择器提高委托事件处理的性能。

回答by Selvakumar Arumugam

Event delegation is a technique to write your handlers before the element actually exist in DOM. This method has its own disadvantages and should be used only if you have such requirements.

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

When should you use event delegation?

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

  1. When you bind a common handler for more elements that needs same functionality. (Ex: table row hover)
    • In the example, if you had to bind all rows using direct bind, you would end up creating n handler for n rows in that table. By using delegation method you could end up handling all those in 1 simple handler.
  2. When you add dynamic contents more frequently in DOM (Ex: Add/remove rows from a table)
  1. 当您为需要相同功能的更多元素绑定公共处理程序时。(例如:表格行悬停)
    • 在示例中,如果您必须使用直接绑定来绑定所有行,您最终会为该表中的 n 行创建 n 个处理程序。通过使用委托方法,您最终可以在 1 个简单的处理程序中处理所有这些。
  2. 当您在 DOM 中更频繁地添加动态内容时(例如:从表中添加/删除行)

Why you should not use event delegation?

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

  1. Event delegation is slower when compared to binding the event directly to element.
    • It compares the target selector on every bubble it hits, the comparison will be as expensive as it is complicated.
  2. No control over the event bubbling until it hits the element that it is bound to.
  1. 与将事件直接绑定到元素相比,事件委托更慢。
    • 它会在它遇到的每个气泡上比较目标选择器,这种比较既昂贵又复杂。
  2. 无法控制事件冒泡,直到它遇到它所绑定的元素。

PS:Even for dynamic contents you don't have to use event delegation method if you are bind the handler after the contents get inserted into DOM. (If the dynamic content be added not frequently removed/re-added)

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

回答by Reincha

Apparently, event delegation is actually recommended now. at least for vanilla js.

显然,现在实际上推荐使用事件委托。至少对于香草 js。

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

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

"Web performance # It feels like listening to every click in the document would be bad for performance, but it's actually more performant than having a bunch of event listeners on individual items."

“Web 性能 # 感觉听文档中的每一次点击都会对性能不利,但实际上它比在单个项目上拥有一堆事件侦听器的性能更高。”

回答by Jules Colle

I'd like to add some remarks and counterarguments to jfriend00's answer. (mostly just my opinions based on my gut feeling)

我想在 jfriend00 的回答中添加一些评论和反驳。(主要是基于我的直觉的我的意见)

No - you should NOT bind all delegated event handlers to the document object. That is probably the worst performing scenario you could create.

First off, event delegation does not always make your code faster. In some cases, it's is advantageous and in some cases not. You should use event delegation when you actually need event delegation and when you benefit from it. Otherwise, you should bind event handlers directly to the objects where the event happens as this will generally be more efficient.

否 - 您不应将所有委托的事件处理程序绑定到文档对象。这可能是您可以创建的性能最差的场景。

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

While it's true that performance might be slightly better if you are only going to register and event for a single element, I believe it doesn't weigh up against the scalability benefits that delegation brings. I also believe browsers are (going to be) handling this more and more efficiently, although I have no proof of this. In my opinion, event delegation is the way to go!

虽然如果您只打算为单个元素注册和事件,性能可能会稍微好一点,但我相信它不会与委托带来的可扩展性优势相提并论。我也相信浏览器(将会)越来越有效地处理这个问题,尽管我没有证据证明这一点。在我看来,事件委托是要走的路!

Second off, you should NOT bind all delegated events at the document level. This is exactly why .live() was deprecated because this is very inefficient when you have lots of events bound this way. For delegated event handling it is MUCH more efficient to bind them to the closest parent that is not dynamic.

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

I kind of agree on this. If you are 100% sure that an event will only happen inside a container, it makes sense to bind the event to this container, but I would still argue against binding events to the triggering element directly.

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

Third off, not all events work or all problems can be solved with delegation. For example, if you want to intercept key events on an input control and block invalid keys from being entered into the input control, you cannot do that with delegated event handling because by the time the event bubbles up to the delegated handler, it has already been processed by the input control and it's too late to influence that behavior.

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

This is simply not true. Please see this codePen: https://codepen.io/pwkip/pen/jObGmjq

这是不正确的。请看这个codePen:https://codepen.io/pwkip/pen/jObGmjq

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

It illustrates how you can prevent a user from typing by registering the keypress event on the document.

它说明了如何通过在文档上注册按键事件来防止用户输入。

Here are times when event delegation is required or advantageous:

When the objects you are capturing events on are dynamically created/removed and you still want to capture events on them without having to explicitly rebind event handlers every time you create a new one. When you have lots of objects that all want the exact same event handler (where lots is at least hundreds). In this case, it may be more efficient at setup time to bind one delegated event handler rather than hundreds or more direct event handlers. Note, delegated event handling is always less efficient at run-time than direct event handlers.

以下是需要或有利事件委托的时候:

当您在其上捕获事件的对象是动态创建/删除的,并且您仍然希望在它们上捕获事件而不必在每次创建新事件处理程序时显式重新绑定事件处理程序。当您有很多对象都需要完全相同的事件处理程序时(其中很多至少是数百个)。在这种情况下,在设置时绑定一个委托的事件处理程序可能比数百个或更多的直接事件处理程序更有效。请注意,委托事件处理在运行时的效率总是低于直接事件处理程序。

I'd like to reply with this quote from https://ehsangazar.com/optimizing-javascript-event-listeners-for-performance-e28406ad406c

我想回复来自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.

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.

Also, googling for performance cost of event delegation googlereturns more results in favor of event delegation.

此外,谷歌搜索performance cost of event delegation google返回更多结果以支持事件委托。

When you're trying to capture (at a higher level in your document) events that occur on any element in the document. When your design is explicitly using event bubbling and stopPropagation() to solve some problem or feature in your page. To understand this a little more, one needs to understand how jQuery delegated event handlers work. When you call something like this:

$("#myParent").on('click', 'button.actionButton', myFn); It installs a generic jQuery event handler on the #myParent object. When a click event bubbles up to this delegated event handler, jQuery has to go through the list of delegated event handlers attached to this object and see if the originating element for the event matches any of the selectors in the delegated event handlers.

Because selectors can be fairly involved, this means that jQuery has to parse each selector and then compare it to the characteristics of the original event target to see if it matches each selector. This is not a cheap operation. It's no big deal if there is only one of them, but if you put all your selectors on the document object and there were hundreds of selectors to compare to every single bubbled event, this can seriously start to hobble event handling performance.

For this reason, you want to set up your delegated event handlers so a delegated event handler is as close to the target object as practical. This means that fewer events will bubble through each delegated event handler, thus improving the performance. Putting all delegated events on the document object is the worst possible performance because all bubbled events have to go through all delegated event handlers and get evaluated against all possible delegated event selectors. This is exactly why .live() is deprecated because this is what .live() did and it proved to be very inefficient.

当您尝试捕获(在文档中的更高级别)发生在文档中任何元素上的事件时。当您的设计明确使用事件冒泡和 stopPropagation() 来解决页面中的某些问题或功能时。要进一步理解这一点,需要了解 jQuery 委托的事件处理程序是如何工作的。当你调用这样的东西时:

$("#myParent").on('click', 'button.actionButton', myFn); 它在 #myParent 对象上安装了一个通用的 jQuery 事件处理程序。当单击事件冒泡到此委托事件处理程序时,jQuery 必须查看附加到此对象的委托事件处理程序列表,并查看事件的原始元素是否与委托事件处理程序中的任何选择器匹配。

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

出于这个原因,您希望设置委托的事件处理程序,以便委托的事件处理程序尽可能接近目标对象。这意味着更少的事件会通过每个委托的事件处理程序冒泡,从而提高性能。将所有委托事件放在文档对象上可能是最糟糕的性能,因为所有冒泡事件都必须经过所有委托事件处理程序,并针对所有可能的委托事件选择器进行评估。这正是 .live() 被弃用的原因,因为这就是 .live() 所做的,它被证明是非常低效的。

Where is this documented? If that's true, then jQuery seems to be handling delegation in a very inefficient way, and then my counter-arguments should only be applied to vanilla JS.

这是在哪里记录的?如果这是真的,那么 jQuery 似乎以一种非常低效的方式处理委托,那么我的反驳应该只适用于 vanilla JS。

Still: I would like to find an official source supporting this claim.

仍然:我想找到支持这一说法的官方消息来源。

:: EDIT ::

:: 编辑 ::

Seems like jQuery is indeed doing event bubbling in a very inefficient way (because they support IE8) https://api.jquery.com/on/#event-performance

似乎 jQuery 确实以非常低效的方式进行事件冒泡(因为它们支持 IE8) https://api.jquery.com/on/#event-performance

So most of my arguments here only hold true for vanilla JS and modern browsers.

所以我这里的大部分论点只适用于 vanilla JS 和现代浏览器。