Event delegation Show archive.org snapshot is a pattern where a container element has a single event listener that handles events for all descendants that match a CSS selector.
This pattern was popularized by jQuery that lets you do this:
$('.container').on('click', '.message', function(event) {
console.log("A message element was clicked!")
})
This technique has some advantages:
- When you have many descendants, you save time by only registering a single listener.
- When the descendants are changed dynamically via JavaScript, no additional event listeners must be registered to enhance new elements.
This technique has some disadvantages:
- In the example above the container receives all click events and must run JavaScript to check if a
.message
element was hit. This can degrade performance when you deal with fast-firing events likescroll
ormousemove
. Performance will be fine with events that fire less frequently, likeclick
,keydown
orsubmit
. - A delegated event listener cannot use
Event#stopPropagation()
Show archive.org snapshot to control the event's bubbling Show archive.org snapshot within its descendants. The event has already bubbled up to the container andstopPropagation()
will only prevent it from bubbling up to the container's own ancestors. - As a conclusion of (2), a delegated listener will always be called after listeners registered directly on a descendant, regardless of the order in which the listeners where added.
Info
jQuery has some complicated code in place to mitigate (2) and (3) for events listeners registered through
jQuery#on()
Show archive.org snapshot .
Native event delegation without jQuery
There is no built-in browser API to do event delegation in the style of
jQuery#on()
Show archive.org snapshot
. However, it is not hard to build this on your own, or use one of many libraries that do it for you.
Building your own delegating event listener
Register a standard event listener to your target. Before you run your callback, check that the actual element (event.target
) matches the desired selector, or has the desired selector in its ancestors:
let container = document.querySelector('.container')
container.addEventListener('click', function(event) {
if (event.target.closest('.message')) {
console.log('A message was clicked!')
}
})
If you do this a lot, you may want to define a function to do it for you:
function addMatchingEventListener(element, eventType, selector, listener, ...optionsArgs) {
element.addEventListener(eventType, ...optionsArgs, (event) => {
let target = event.target
if (target && target.closest(selector)) {
listener.call(this, event)
}
})
}
Use it like this:
addMatchingEventListener(container, 'click', '.message', function(event) {
console.log("A message was clicked!")
}
With Unpoly
If you're using
Unpoly
Show archive.org snapshot
you can use
up.on()
Show archive.org snapshot
to register a delegating event listener:
up.on(container, 'click', '.message', function(event) {
console.log("A message was clicked!")
}
Also see