I'd like to add some remarks and counterarguments to jfriend00's answer. (mostly just my opinions based on my gut feeling)
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 an event for a single element, I believe it doesn't outweigh 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.
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.
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
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
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 google
returns more results in favor of event delegation.
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.
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.
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
So most of my arguments here only hold true for vanilla JS and modern browsers.