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.
onCardClicked
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.
event.composedPath()
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 event.composedPath()
.
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 event.nativeEvent.composedPath()
.
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.