First published: 28/08/2024
Adding event listeners to DOM nodes can incur performance and memory costs. You might not notice it at first, but your users on low-powered devices will, especially if you add and remove dozens (or even hundreds) of event listeners.
In the following snippet, I’m querying a single input element and adding an event listener to it.
Let’s have a look at the memory consumption for one input.
The browser stores the event name, reference to the function, and reference to the DOM node in memory. For the most simple of event listeners. The two properties worth noting are “Shallow Size” and “Retained Size”, and the number of event listeners.
In this case, there are 9 event listeners set on the page (the browser automatically applies a few, and your extensions may add additional ones too).
Let’s break down what’s going on above:
Shallow Size - The size of the object that’s stored in memory.
Retained Size - The total amount of memory used by the object, and any objects that reference it. This number indicates how much memory can be freed if the object is deleted
The more event listeners you use, the more memory your app will require. The costs start off small, but can explode if you’re using heavy frameworks and you’re not optimising your JavaScript.
Let’s look at an example. If you’re building the table of contents for a video course, you might decide to have a checkmark next to a video’s title, to let a user mark the video as watched. The markup could look like this:
You would then query the list of inputs and add an event listener to each and every one.
You can see how this affects memory consumption in the browser:
The browser needs to keep track of 1000 event listeners, which increases the shallow size by over 10,000%.
I know that this is a contrived example, but you can see how things can escalate for a complex web application. Fortunately, there’s a straightforward optimisation you can make to massively reduce the memory consumption of your event listeners, event delegation.
Instead of adding the same event listener to every input element, you can add a single event listener to a common ancestor.
This is when you add a single event listener to a common ancestor instead of on all the child elements. Inside of the event handler, you can check to see whether the target element was a button, and if so, perform the same logic you would have otherwise done.
Let’s take a look at the memory usage now
The browser no longer keeps track of 1000 event listeners in memory.
Even the the memory consumption in this example was small, it’s still important to be mindful especially when you’re building heavier applications in a more JavaScript-intensive framework.
If you enjoyed this article, then I figure you like to be kept in the loop with web development tips. If so, the Component Odyssey newsletter might be right up your alley.