In this article, we will consider event delegation and what it is for. How it can help you in understanding the mechanisms of the framework, and maybe even in improving your decisions in pure JS.
Unfortunately, in our modern world, there is no way to delve into the little things and how frameworks work.
We use many tools and like how easy it is to work with them. And most importantly, these tools solve our problems. What else do you need? Of course, the story ends here. How little is needed for happiness?
But when you implement something on pure JS, you begin to wonder how those frameworks and tools solve certain problems.
In this article, we will consider event delegation and what it is for. How it can help you in understanding the mechanisms of the framework, and maybe even in improving your decisions in pure JS.
What is an event delegation?
You have probably already encountered delegation of authority and tasks. The meaning of this word is very important, isn’t it? And the main point is in centralizing decision-making, thereby reducing the number of decision-making points. This is the work of a leader.
Perhaps the key points of delegation:
- Centralization which makes it possible to monitor events
- Tracking from whom the event came
- Filtering decide to react or not
In fact, you do not handle an event on each child DOM element, but handle them on some parent DOM element and then decide what to do and how.
Event delegation – is a method of attaching event handlers not to the elements from which you actually want to handle events, but to a higher-level element.
When to apply?
In any case, when you work with a large number of interactive elements. We get a lot of elements that can be removed and added quite intensively. If you accidentally did not clear the events of an element that was previously removed from the DOM by any line of code, you will get zombies that can eat up the memory.
The solution to the problem of handling events in parent elements with a large nesting of child elements. For example, if you click on the most nested child element, click events will be triggered on each of the parent elements. And here you can avoid using e.stopPropagation(). This is why it is not recommended to use event chain interruption in handlers, since if you need to handle an event on the parent component, then you will not succeed.
How is event delegation applied in React?
Actually, you are watching a very good picture. There are practices that have long been applied that really solve problems, and you most often see their usage in high-quality and recommended products. These are kind of design patterns.
Let’s see how React
applies Event delegation
: https://stackblitz.com/edit/react-7h5cje?embed=1&file=index.js
First, open the Chrome Developer Tool
sand find the list item.
We find for example the first element in the list
Highlight this list item in the DOM
and go to the Event Listeners
tab.
Event Listeners tab and click event list
If you expand the list of click
event handlers, you will notice that React-DOM
has created two handlers:
- One on our element selected in the
DOM
- And one event handler for the
DOM document
object globally
Hmm …
you think, every element has a handler, in this case, where are the pros of Event Delegation
?
That’s a fair question, but in each case you need to dig down. Let’s go deeper and expand the property tree of the <li>
element
Event Listener →li
What do we see? The name of the noop
function hints that this is a stub
. And most importantly, this stub
exists in a single, global instance, and when you delete an item from the DOM
, you can easily remove the element.removeEventListener(‘click’, noop)
event. Yes, this slightly eliminates the effect of Event Delegation
, but if you search the Internet, you will find the answer to this question. I’ll save your time — this decision is associated with a bug in iOS Safari<span class="outer-link__count"></span>
.
A bit about the bug in iOS Safari
For each so-called "bug," because it violates our usual use case, there is a certain reason. Because this bug can be the result of an architectural solution and hardware capabilities.
It turns out that
Safari
on theiPhone
does not supportevent delegation
for click events, unless click occurs on alink
ortext field
.Nevertheless,
Apple
engineers could not be so wrong, here the matter is something else. There must be somereasons
for thisbehavior
. The true reason is not known, but perhaps — this is a memory management problem. Apparently, giving all the elements on the page the ability to respond to clicks requires too many resources, and Apple engineers decided to disable this possibility.
This fact is, of course, a serious problem for web pages with a high degree of user interaction. This is an annoying fact, but, fortunately, there is a workaround.
You must make the element clickable by providing it with its own onclick
event handler. This handler can be empty
, while it is present, any element will be clickable.
document.onclick = function (e) { // click logic here } div.onclick = function () {} // empty event handler
We still handle the event at the document level, but add an empty event handler
to the div
that we want to click. Now suddenly a div
becomes clickable
, the event passed to the document and is processed correctly.
The only trick is that we have to repeat this every time we change the div. After the old div has been removed from the DOM
and a new one has been added, the onclick
event handler also needs to be reinstalled.
Now you know the answer to the question: Where did the noop()
handler come from for the li
element?
You can also notice the mention of a safari bug in the source code React
See here.
Fun fact, but //TODO: Only do this for the relevant Safaris maybe?
hints that it would be nice to use this trick only on bug-prone versions of Safari
. Since we see the ubiquitous installation of noop
for those elements that require a click event, it means that no mechanism has yet been added to narrow the place of usage of this trick.
This fact, of course, does not improve the mood of the developer, since it still creates a load on the use of memory on a page with many components, which suggests receiving a feedback from the user in the form of a click
.
Let's back to the event delegation topic in React
So, we found out why the noop()
event handler is needed. And now we can understand why the event is handled globally on document object.
React's global event handler by event delegation approach
Event delegation is an important event processing design pattern. This is where the magic of event delegation is going on.
Let's take a look at the event handling method in the React source.
See here.
And if we delve deeper into this super-method in the React source, you can see the following picture:
Here is a very long function for determining the target that issued the event, and further dispatching the event through the React infrastructure.
As you can see, the basic logic for determining the element that generated the event, as well as throwing the event through the React architecture, is done below.
And if we look, purely from interest, at the getEventTarget
function, we will see how much we need to consider of moments before choosing the right element, which is suitable as the source of the event.
See here.
As a result, you have to pay for the convenience in handling events with a more complex code scheme. But if you look at the hell of the handlers that could wait for the developer, then nevertheless the Event Delegation
rules. The main thing is to understand the general pattern, and its usage is a matter of technology.
Conclusion
After reading this article, you now know:
- What is
event delegation
? - How it works in general in
React
; - Why you need a
noop
click event handler; - The details of the so-called
bug
iniOS Safari
; - The implementation of
event delegation
is a centralized event processing pattern.
I hope you learned something interesting for yourself, and if not, it means you may have consolidated your knowledge.
Thanks for reading!