Event delegation
Capturing and bubbling allow us to implement one of the most powerful event handling patterns called event delegation. The idea is that if we have a lot of elements handled in a similar way, then instead of assigning a handler to each of them – we put a single handler on their common ancestor. In the handler we get event.target to see where the event actually happened and handle it. Let’s see an example – the Ba-Gua diagram reflecting the ancient Chinese philosophy. Here it is: [iframe height=350 src=“bagua” edit link] The HTML is like this: The table has 9 cells, but there could be 99 or 9999, doesn’t matter. Our task is to highlight a cell
| dynamically at any time and the highlighting will still work. Still, there’s a drawback. The click may occur not on the | , but inside it. In our case if we take a look inside the HTML, we can see nested tags inside | , like :
Naturally, if a click happens on that then it becomes the value of event.target.
In the handler table.onclick we should take such event.target and find out whether the click was inside | or not.
Here’s the improved code:
Explanations:
1. The method elem.closest(selector) returns the nearest ancestor that matches the selector. In our case we look for | on the way up from the source element.
2. If event.target is not inside any | , then the call returns immediately, as there’s nothing to do.
3. In case of nested tables, event.target may be a | , but lying outside of the current table. So we check if that’s actually our table’s | .
4. And, if it’s so, then highlight it.
As the result, we have a fast, efficient highlighting code, that doesn’t care about the total number of | in the table.
| Delegation example: actions in markupThere are other uses for event delegation. Let’s say, we want to make a menu with buttons “Save”, “Load”, “Search” and so on. And there’s an object with methods save, load, search… How to match them? The first idea may be to assign a separate handler to each button. But there’s a more elegant solution. We can add a handler for the whole menu and data-action attributes for buttons that has the method to call: The handler reads the attribute and executes the method. Take a look at the working example: Please note that this.onClick is bound to this in (*). That’s important, because otherwise this inside it would reference the DOM element (elem), not the Menu object, and this[action] would not be what we need. So, what advantages does delegation give us here? We could also use classes .action-save, .action-load, but an attribute data-action is better semantically. And we can use it in CSS rules too. The “behavior” patternWe can also use event delegation to add “behaviors” to elements declaratively, with special attributes and classes. The pattern has two parts: 1. We add a custom attribute to an element that describes its behavior. 2. A document-wide handler tracks events, and if an event happens on an attributed element – performs the action. Behavior: CounterFor instance, here the attribute data-counter adds a behavior: “increase value on click” to buttons: If we click a button – its value is increased. Not buttons, but the general approach is important here. There can be as many attributes with data-counter as we want. We can add new ones to HTML at any moment. Using the event delegation we “extended” HTML, added an attribute that describes a new behavior. Behavior: TogglerOne more example of behavior. A click on an element with the attribute data-toggle-id will show/hide the element with the given id: Let’s note once again what we did. Now, to add toggling functionality to an element – there’s no need to know JavaScript, just use the attribute data-toggle-id. That may become really convenient – no need to write JavaScript for every such element. Just use the behavior. The document-level handler makes it work for any element of the page. We can combine multiple behaviors on a single element as well. The “behavior” pattern can be an alternative to mini-fragments of JavaScript. SummaryEvent delegation is really cool! It’s one of the most helpful patterns for DOM events. It’s often used to add the same handling for many similar elements, but not only for that. The algorithm: 1. Put a single handler on the container. 2. In the handler – check the source element event.target. 3. If the event happened inside an element that interests us, then handle the event. Benefits: The delegation has its limitations of course: <table>
<tr>
<th colspan="3"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>
</tr>
<tr>
<td class="nw"><strong>Northwest</strong><br>Metal<br>Silver<br>Elders</td>
<td class="n">...</td>
<td class="ne">...</td>
</tr>
<tr>...2 more lines of this kind...</tr>
<tr>...2 more lines of this kind...</tr>
</table>
Example:
Follow the lesson from Microsoft Web-Dev-For-Beginners course |