In the previous blog, we discussed Event Bubbling. When an event occurs on a nested element, it first triggers on the element itself and then bubbles up to its parent elements. Without understanding event bubbling, this behavior might cause unintended issues. However, when used effectively, it can become a powerful tool.
- Common Ancestor
- Data Attributes: data-*
- Combining Data Attributes with event.target
Common Ancestor
When we have many child elements that share the same behavior, it’s inefficient to add target.addEventListener("click", () => {})
to each individual child element. Instead, we can attach the event listener to their common parent (using event.currentTarget
). Then, regardless of which child (event.target
) is clicked, the event handler will execute.
Here is an example from MDN:
HTML
JS
function random(number) {
return Math.floor(Math.random() * number);
}
function bgChange() {
const rndCol = `rgb(${random(255)} ${random(255)} ${random(255)})`;
return rndCol;
}
const container = document.querySelector("#container");
container.addEventListener("click", (event) => {
event.target.style.backgroundColor = bgChange();
});
Data Attributes: data-*
The data-*
attribute allows you to create custom attributes in HTML. These attributes make it easier to exchange information between HTML and the DOM.
While it still requires adding data-*
to each child element, it offers several advantages. Not only can it be used for CSS styling, but it also simplifies management compared to adding individual event listeners to each child element.
The Modern JavaScript Tutorial provides an elegant example:
Save
Load
Search
class Menu {
constructor(elem) {
this._elem = elem;
elem.onclick = this.onClick.bind(this); // (*)
}
save() {
alert('saving');
}
load() {
alert('loading');
}
search() {
alert('searching');
}
onClick(event) {
let action = event.target.dataset.action;
if (action) {
this[action](); }
};
}
new Menu(menu);
Combining Data Attributes with event.target
Using data attributes together with event.target
, we can set independent behaviors for elements.
Quick Review: event.target
A reference to the object to which the event was originally dispatched.
Here's an example:
Counter:
One more counter:
document.addEventListener('click', function(event) {
if (event.target.dataset.counter != undefined) { // if the attribute exists...
event.target.value++;
}
});
The Modern JavaScript Tutorial
In this example, both buttons use the same data-counter
attribute and share a single event listener. However, referencing event.target
ensures that they behave independently. Each button retains its own state (value), making this a powerful and efficient approach.
Event bubbling, combined with techniques like using a common ancestor, data-* attributes, and event.target, provides a powerful and efficient way to manage events in JavaScript. Mastering these concepts ensures that events can be handled cleanly and effectively.