I’m slowly migrating projects away from jQuery. It has served me really well in the past, and it still works, but it is no longer necessary. One of the few things that have been bothering me though was the lack of ‘live’-attaching of EventListeners. jQuery allows you to bind an onsubmit
event listener to any <form>
, even <form>
s that haven’t been added to the DOM yet. It used to be called live()
(from version 1.3 to 1.9), but the functionality was later reintroduced with on()
. You would bind the event listener not to a <form>
directly, but to a container (up to document).
$(document).on('click', 'p.alert', function(e){
confirm("Did you read this carefully?"); }
);
Moving away from jQuery, we use the ‘standard’ addEventListener()
: but it has no easy way of delegating the events.
matches
Element.querySelector(".CSS.selector.here")
is probably quite well known by know, but I was unaware of the matches()
method, also available on any element.
Given this simple html:
<body><p>Test</p><p class="alert">Test 1</p></body>
We could get all p
elements simply by:
var ps = document.getElementsByTagName("p") // Returns a HTMLCollection (basically a NodeList for HTMLElements only)
or with the modern (but probably somewhat slower CSS-style selector):
var ps = document.querySelectorAll("p") // Returns a NodeList
The matches operation allows us to check wether the returned node would match another CSS-selector.
ps[0].matches("p") && ps[1].matches("p") // return: true
ps[1].matches("p.alert") // returns: true
ps[0].matches("p.alert") // returns: false
This is useful for catching the delegated event jQuery-style (using CSS-selectors)
Note that IE hasn’t implemented the matches method using its standard name, even in the latest versions. Hence a polyfill is required (abridged, from MDN)
if (!Element.prototype.matches) {
Element.prototype.matches = Element.prototype.msMatchesSelector
}
Event delegation is delegating the catching of an event to an element higher up the hierarchy. Tell document
to catch errors for a form
. I’m not fan of this abstract wording that practically results in automatic binding of events (and considering this is the end-goal, you might actually think actually listening for DOM changes and binding it directly on DOM-change might also be a viable alternative to the approach presented here, but hey, this is simplest for now)
document.addEventListener('click', (e) => {
if (e.currentTarget.matches("p.alert")) {
console.log("hit")
}
});
I don’t like this as much as the jQuery listener.
If it is you know what you’re doing, I think it is perfectly fine to implement the following (I don’t want to replace my $
’s with _
1 (or whatever) either, so there we go):
HTMLDocument.prototype.addDelegatedEventListener = function(event, matcher, cb) {
var base = this;
var newCB = function(event) {
if (event.target.matches(matcher)) {
return cb(event);
}
};
base.addEventListener(event, newCB);
}
This allows you to simply write, which suits me well:
document.addDelegatedEventListener('click', "p.alert", (e) => {
console.log("hit new")
});
Enjoy. Codepen here.
1I’m referring here to the popular Underscore JavaScript library that “provides a whole mess of useful functional programming helpers without extending any built-in objects.” Well, I do like extending built-in objects. That’s what prototype is made for. Really: when this method name has gotten another implementation some day it won’t matter if the project doesn’t get any attention. If it is still being maintained, find and replace it if it bothers you2 and get back to work. It is nothing compared to switching jQuery or underscore-like frameworks.
2As you might have seen, I try to stay close to the most the API of the addEventListener-method. This to improve chances that if browsermakers would decide to add this functionality, simply removing this code might even work.
Enjoyed this? Follow me on Mastodon or add the RSS, euh ATOM feed to your feed reader.
Dit artikel van murblog van Maarten Brouwers (murb) is in licentie gegeven volgens een Creative Commons Naamsvermelding 3.0 Nederland licentie .