quiz/js: update event bubbling and delegation quiz solution (#440)
This commit is contained in:
parent
cdd4d2ba0b
commit
f2b326c982
|
|
@ -2,6 +2,18 @@
|
|||
title: Describe event bubbling
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
**Event bubbling** is a DOM event propagation mechanism where an event, like a click, starts at the target element and bubbles up to the root of the document. This allows ancestor elements to also respond to the event.
|
||||
|
||||
**Practical Use:** Event bubbling is essential for event delegation, where a single event handler manages events for multiple child elements, enhancing performance and code simplicity.
|
||||
|
||||
**Pitfall:** Failing to manage event propagation can lead to unintended behavior, such as multiple handlers firing for a single event.
|
||||
|
||||
---
|
||||
|
||||
## What is Event Bubbling?
|
||||
|
||||
Event bubbling is a propagation mechanism in the DOM (Document Object Model) where an event, such as a click or a keyboard event, is first triggered on the target element that initiated the event and then propagates upward (bubbles) through the DOM tree to the root of the document.
|
||||
|
||||
## Bubbling phase
|
||||
|
|
@ -28,18 +40,154 @@ child.addEventListener('click', () => {
|
|||
});
|
||||
```
|
||||
|
||||
When you click the "Click me!" button, both the child and parent event handlers will be triggered due to event bubbling.
|
||||
When you click the "Click me!" button, both the child and parent event handlers will be triggered due to the event bubbling.
|
||||
|
||||
## Stopping propagation
|
||||
|
||||
Event propagation can be stopped during the bubbling phase using the `stopPropagation()` method. If an event handler calls `stopPropagation()`, it prevents the event from further bubbling up the DOM tree, ensuring that only the handlers of the elements up to that point in the hierarchy are executed.
|
||||
|
||||
```js
|
||||
child.addEventListener('click', (event) => {
|
||||
console.log('Child element clicked');
|
||||
event.stopPropagation();
|
||||
});
|
||||
```
|
||||
|
||||
## Event delegation
|
||||
|
||||
Event bubbling is the basis for a technique called "event delegation", where you attach a single event handler to a common ancestor of multiple elements and use event delegation to handle events for those elements efficiently. This is particularly useful when you have a large number of similar elements, like a list of items, and you want to avoid attaching individual event handlers to each item.
|
||||
```js
|
||||
parent.addEventListener('click', (event) => {
|
||||
if (event.target && event.target.id === 'child') {
|
||||
console.log('Child element clicked');
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## References
|
||||
## Benefits
|
||||
|
||||
- **Cleaner Code:** Reduced number of event listeners improves code readability and maintainability.
|
||||
- **Efficient Event Handling:** Minimizes performance overhead by attaching fewer listeners.
|
||||
- **Flexibility:** Allows handling events happening on child elements without directly attaching listeners to them.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- **Accidental Event Handling:** Be mindful that parent elements might unintentionally capture events meant for children. Use `event.target` to identify the specific element that triggered the event.
|
||||
- **Event Order:** Events bubble up in a specific order. If multiple parents have event listeners, their order of execution depends on the DOM hierarchy.
|
||||
- **Over-delegation:** While delegating events to a common ancestor is efficient, attaching a listener too high in the DOM tree might capture unintended events.
|
||||
|
||||
## Use Cases
|
||||
|
||||
Here are some of the practical use cases with code examples.
|
||||
|
||||
### Reducing Code with Event Delegation
|
||||
|
||||
Imagine you have a product list with numerous items, each with a "Buy Now" button. Traditionally, you might attach a separate click event listener to each button:
|
||||
|
||||
```js
|
||||
// HTML:
|
||||
// <ul id="product-list">
|
||||
// <li><button id="item1-buy">Buy Now</button></li>
|
||||
// <li><button id="item2-buy">Buy Now</button></li>
|
||||
// </ul>
|
||||
|
||||
const item1Buy = document.getElementById("item1-buy");
|
||||
const item2Buy = document.getElementById("item2-buy");
|
||||
|
||||
item1Buy.addEventListener("click", handleBuyClick);
|
||||
item2Buy.addEventListener("click", handleBuyClick);
|
||||
|
||||
// ... repeat for each item ...
|
||||
|
||||
function handleBuyClick(event) {
|
||||
console.log("Buy button clicked for item:", event.target.id);
|
||||
}
|
||||
```
|
||||
|
||||
This approach becomes cumbersome as the number of items grows. Here's how event bubbling can simplify things:
|
||||
|
||||
```js
|
||||
// HTML:
|
||||
// <ul id="product-list">
|
||||
// <li><button id="item1-buy">Buy Now</button></li>
|
||||
// <li><button id="item2-buy">Buy Now</button></li>
|
||||
// </ul>
|
||||
|
||||
const productList = document.getElementById("product-list");
|
||||
|
||||
productList.addEventListener("click", handleBuyClick);
|
||||
|
||||
function handleBuyClick(event) {
|
||||
// Check if the clicked element is a button within the list
|
||||
if (event.target.tagName.toLowerCase() === "button") {
|
||||
console.log("Buy button clicked for item:", event.target.textContent);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By attaching the listener to the parent (`productList`) and checking the clicked element (`event.target`) within the handler, you achieve the same functionality with less code. This approach scales efficiently as you add more items.
|
||||
|
||||
### Dropdown Menus
|
||||
|
||||
Consider a dropdown menu where clicking anywhere on the menu element (parent) should close it. With event bubbling, you can achieve this with a single listener:
|
||||
|
||||
```js
|
||||
|
||||
// HTML:
|
||||
// <div id="dropdown">
|
||||
// <button>Open Menu</button>
|
||||
// <ul>
|
||||
// <li>Item 1</li>
|
||||
// <li>Item 2</li>
|
||||
// </ul>
|
||||
// </div>
|
||||
|
||||
const dropdown = document.getElementById("dropdown");
|
||||
|
||||
dropdown.addEventListener("click", handleDropdownClick);
|
||||
|
||||
function handleDropdownClick(event) {
|
||||
// Close the dropdown if clicked outside the button
|
||||
if (event.target !== dropdown.querySelector("button")) {
|
||||
console.log("Dropdown closed");
|
||||
// Your logic to hide the dropdown content
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here, the click event bubbles up from the clicked element (button or list item) to the `dropdown` element. The handler checks if the clicked element is not the button and closes the menu accordingly.
|
||||
|
||||
### Accordion Menus
|
||||
|
||||
Imagine an accordion menu where clicking a section header (parent) expands or collapses the content section (child) below it. Event bubbling makes this straightforward:
|
||||
|
||||
```js
|
||||
// HTML:
|
||||
// <div class="accordion">
|
||||
// <div class="header">Section 1</div>
|
||||
// <div class="content">Content for Section 1</div>
|
||||
// <div class="header">Section 2</div>
|
||||
// <div class="content">Content for Section 2</div>
|
||||
// </div>
|
||||
|
||||
const accordion = document.querySelector(".accordion");
|
||||
|
||||
accordion.addEventListener("click", handleAccordionClick);
|
||||
|
||||
function handleAccordionClick(event) {
|
||||
// Check if clicked element is a header
|
||||
if (event.target.classList.contains("header")) {
|
||||
const content = event.target.nextElementSibling;
|
||||
content.classList.toggle("active"); // Toggle display of content
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By attaching the listener to the `accordion` element, clicking on any header triggers the event. The handler checks if the clicked element is a header and toggles the visibility of the corresponding content section.
|
||||
|
||||
## Further reading
|
||||
|
||||
- [MDN Web Docs on Event Bubbling](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_bubbling)
|
||||
- [JavaScript.info - Bubbling and Capturing](https://javascript.info/bubbling-and-capturing)
|
||||
- [W3C DOM Level 3 Events Specification](https://www.w3.org/TR/DOM-Level-3-Events/#event-flow)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,16 @@
|
|||
title: Explain event delegation
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
**Event delegation** is a JavaScript design pattern where a single event listener is attached to a common ancestor to manage events for multiple child elements efficiently.
|
||||
|
||||
**Practical Use:** It's essential for handling events on dynamic or numerous elements, such as list items, without attaching individual listeners to each.
|
||||
|
||||
**Benefit:** Event delegation improves performance, reduces memory usage, and simplifies code maintenance.
|
||||
|
||||
**Pitfall:** Incorrectly identifying the event target can lead to unintended behavior.
|
||||
|
||||
---
|
||||
Event delegation is a design pattern in JavaScript used to efficiently manage and handle events on multiple child elements by attaching a single event listener to a common ancestor element. This pattern is particularly valuable in scenarios where you have a large number of similar elements, such as list items, and want to streamline event handling.
|
||||
|
||||
## How Event Delegation works
|
||||
|
|
@ -43,11 +53,80 @@ In this example, a single click event listener is attached to the `<ul>` element
|
|||
|
||||
Event delegation is commonly used in scenarios like:
|
||||
|
||||
- Managing lists, menus, or tables with many items or rows.
|
||||
- Handling dynamic content in single-page applications.
|
||||
- Simplifying code by avoiding the need to attach and remove event listeners for elements that change.
|
||||
### Managing lists, menus, or tables with many items or rows
|
||||
|
||||
## Resources
|
||||
```js
|
||||
// HTML:
|
||||
// <ul id="item-list">
|
||||
// <li>Item 1</li>
|
||||
// <li>Item 2</li>
|
||||
// <li>Item 3</li>
|
||||
// </ul>
|
||||
|
||||
const itemList = document.getElementById('item-list');
|
||||
|
||||
itemList.addEventListener('click', (event) => {
|
||||
if (event.target.tagName === 'LI') {
|
||||
console.log(`Clicked on ${event.target.textContent}`);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
In this example, a single click event listener is attached to the `<ul>` element. When a click event occurs on an `<li>` element, the event bubbles up to the `<ul>` element, where the event listener checks the target's tag name to identify which list item was clicked.
|
||||
|
||||
### Handling dynamic content in single-page applications
|
||||
```js
|
||||
// HTML:
|
||||
// <div id="button-container">
|
||||
// <button>Button 1</button>
|
||||
// <button>Button 2</button>
|
||||
// </div>
|
||||
// <button id="add-button">Add Button</button>
|
||||
|
||||
const buttonContainer = document.getElementById('button-container');
|
||||
const addButton = document.getElementById('add-button');
|
||||
|
||||
buttonContainer.addEventListener('click', (event) => {
|
||||
if (event.target.tagName === 'BUTTON') {
|
||||
console.log(`Clicked on ${event.target.textContent}`);
|
||||
}
|
||||
});
|
||||
|
||||
addButton.addEventListener('click', () => {
|
||||
const newButton = document.createElement('button');
|
||||
newButton.textContent = `Button ${buttonContainer.children.length + 1}`;
|
||||
buttonContainer.appendChild(newButton);
|
||||
});
|
||||
```
|
||||
|
||||
In this example, a click event listener is attached to the `<div>` container. When a new button is added dynamically and clicked, the event listener on the container handles the click event.
|
||||
|
||||
### Simplifying code by avoiding the need to attach and remove event listeners for elements that change
|
||||
|
||||
```js
|
||||
// HTML:
|
||||
// <form id="user-form">
|
||||
// <input type="text" name="username" placeholder="Username">
|
||||
// <input type="email" name="email" placeholder="Email">
|
||||
// <input type="password" name="password" placeholder="Password">
|
||||
// </form>
|
||||
|
||||
const userForm = document.getElementById('user-form');
|
||||
|
||||
userForm.addEventListener('input', (event) => {
|
||||
const { name, value } = event.target;
|
||||
console.log(`Changed ${name}: ${value}`);
|
||||
});
|
||||
```
|
||||
|
||||
In this example, a single input event listener is attached to the form element. It handles input changes for all child input elements, simplifying the code by avoiding multiple event listeners.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- **Incorrect Target Handling:** Ensure correct identification of the event target to avoid unintended actions.
|
||||
- **Event Overhead:** While event delegation is efficient, handling complex logic within the event listener can introduce overhead if not managed properly.
|
||||
|
||||
## Further reading
|
||||
|
||||
- [MDN Web Docs on Event Delegation](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_delegation)
|
||||
- [JavaScript.info - Event Delegation](https://javascript.info/event-delegation)
|
||||
|
|
|
|||
Loading…
Reference in New Issue