Listening For Events in JavaScript: Closure vs. Delegation
When starting out with JavaScript, manipulating the DOM may seem straightforward, but as with all things JS, there are many different paths that can get you to the same destination.
With event listeners, we’re able to “listen” for certain interactions, or “events”, that the user has with the DOM. We can listen for things like “click”, “keypress”, and “hover”, and in turn cause different pieces of code to run. This is ideal for elements where we need something to happen when the user clicks them, like <li> elements.
Delegation
Just as it sounds, using delegation means delegating the event listening to a single stable HTML element (in the source HTML), and letting that element worry about listening for events happening to its children.
Let’s say we had a stable <ul> element and we wanted to listen for events happening to the multiple <li> elements nested within. We could add an event listener directly to the <ul>, and by using conditionals we could run different code based on different events:
In the above example, we’re telling the most stable parent element, the <ul>, to listen for “clicks” in its space. If the event’s (click) target is a “delete” button, we’re going to remove that button’s closest related <li>. However, if the click event target matches an <li>, meaning if where the user clicked happened to be an <li> within this <ul>, then change the color of that <li>’s text to blue.
Delegation allows us to create a single event listener on a single stable element, and by doing so, use less memory in our application (ideal for scalable projects). We can also rest easy knowing that our event listener is on a stable element, and thus should always be there.
It’s important to note that delegation can get tricky, especially when we use methods like .matches() and .closest(). Be sure to console.log() and debug as you go to make sure you’re manipulating the correct DOM elements.
Closure
The concept of “closure” deals with much more than just event listening, but leveraging it here can be very helpful for those beginning JavaScript and DOM manipulation.
Essentially, closures decide which variables are available to us within functions. This is especially convenient when we’re adding elements to the DOM with JavaScript (unstable elements; ones that may not always be there). Let’s refactor our earlier code to demonstrate using closure:
When using delegation, we told the <ul> to listen for events happening inside of it, including clicks to the delete button. In the above example, we are using JS to add <li> elements to our stable <ul>, as well as adding a <button> complete with its own event listener.
Closure lets our .addEventListener()’s callback function have access to variables a level up in the scope chain, meaning that the <button> we created is available to be used within. So when each <li> is born, it’s born with a <button>, and when that <button> is born, it’s born with an event listener that knows exactly whom it belongs to. No guessing needed here.
When it comes to event listening and closure, beginners might find it easier to understand what’s being manipulated on the DOM when there are less guesses to be made. However, keep in mind that having many event listeners could affect the scalability of your project.
So, What’s Better?
There’s no right answer to this! But both have their use cases, and a good rule of thumb is to stick to delegating your event listener to stable HTML elements. Leveraging closure is useful when event listeners are needed on DOM elements you’re adding with JavaScript. Stick to this convention and you won’t run into issues like multiple event listeners on the same element.
Final Thoughts
As we know, the beauty of JavaScript is its flexibility and room for creativity, but these can also get us into rabbit holes of confusing behavior and errors, and not only in event listening with delegation or closure. Be sure to constantly console.log() and debug your variables and code to verify you’re working with what you expect, and when in doubt, always check the documentation. Happy coding!