When rendering a list of clickable things, where should the click handler live? Typically click handlers are found on the child component, but what if there was a better way?
For our sample problem, we'll have 3 clickable cards and when any card is clicked, a message is shown.
The most straight-forward implementation is for the child component to offer the prop
onCardClicked, which is called when the card is clicked.
That is an awful amount of ceremony. We can do better. We can go higher-order.
If all the child component is doing is creating a new handler, we can simply do that in the parent.
This is better, but we are still creating an EventListener for each card. It doesn't matter when there are a small number of cards, but EventListeners are not free. Each one takes a little bit of memory.
But if we only had a single EventListener on
main, how do we know which card was clicked? This is where
event.composedPath() comes into play.
In HTML, elements contain each other. When an element is clicked, an event bubbles from the clicked element to its parent recursively until we reach the top-level element.
The order in which the event will be bubbled can be queried by
We can use
event.composedPath() in the EventListener to figure out which child element was clicked. The only problem is
event.composedPath() returns native HTMLElements. While in theory we could simply read
innerText to see which card was clicked, however the more general solution is to record information as data attributes.
Note that React wraps native Events with their own synthetic events. This is why we use
Another neat thing with
event.composedPath() is the
onClick handler doesn't need to be the immediate parent of the clicked element. This makes this pattern more resilient to change.
Runnable code samples also include a Vanilla JS version.
Do you write robust event handlers? We'd like to hear from you. Battlefy is hiring.