1 introduction
For the following code:
<div class="Grandpa">
<div class="Dad">
<div class="Son">The text</div>
</div>
</div>
Copy the code
Add event listener fn1, fn2, fn3 to grandfather div, father div, son div
- Click on the text, calculate not click son?
- Click on the text, does that count as clicking dad?
- Click text, does it count as click grandpa?
Answer: Both
Question 2:
- Click on the text to call fn1, fn2, or fn3 first?
Answer: Either way, if you’re trapping, you call Fn1 from the outside in and if you’re bubbling, you call FN3 from the inside out
Let’s take a closer look at DOM event mechanisms and event delegates
2 DOM event mechanism
The DOM event mechanism mainly consists of two phases: capture phase and bubbling phase
2.1 What is capture and bubbling?
When an event occurs on an element that has a parent element, modern browsers run two different phases – the capture phase and the bubble phase. In the capture phase:
- The browser checks the outermost ancestor of the element
<html>
, whether one was registered during the capture phaseonclick
Event handler, if so, run it. - And then, it moves to
<html>
Click on the element’s next ancestor and do the same, then click on the element and the next ancestor, and so on until you reach the actual clicked element.
In the bubbling stage:
- The browser first examines the clicked element (in the initial example, text) and then sees if it is present during the bubble phase
Onclick
Event, if so, run - Then, look for the next one
parentNode
, (in the initial example, the son div) - And then see if there are any in the bubbling phase
Onclick
Event, if so, run
AddEventListener (‘click’, fn, bool) if bool is not passed, Or false, we’ll call fn in the bubble phase and if the third parameter Bool is true, we’ll call fn in the capture phase
2.2 Cancel bubbling
Capture cannot be undone, but bubble can be undone, e.propagation() can. However, some events cannot be undone, such as scroll event, which can be queried on MDN
2.3 Differences between Target and currentTarget
E.telget The element the user is working on. E.currenttarget The element the programmer is listening on
Here’s an example:
<div>
<span>The text</span>
</div>
Copy the code
Let’s say we’re listening for a div, but the user is actually clicking on text. E.target is the SPAN tag. E.trenttarget is the div tag
2.4 Summary capture and bubbling:
Capture: When the user clicks a button, the browser traverses the window from top to bottom, triggering event handlers one by one. Bubbling: The browser traverses the window from the bottom up to the button clicked by the user, triggering event handlers one by one.
3 Event Delegation
3.1 What is Event delegation
Because of the bubble phase, the browser triggers event handlers one by one as it traverses from the bottom up to the window from what the user clicked, it can listen for an ancestor node (for example, a parent node, a grandparent node) to process events from multiple child nodes at the same time
3.2 Common Application Scenarios
Scenario 1: What do we do if we want to add click events to 100 buttons? The dumbest way to do this is to delegate events to each of the 100 buttons: listen for the dad of the 100 buttons, wait for bubbles, and determine if target is one of the 100 buttons
Scenario 2: We want to listen for click events for elements that do not currently exist. What do we do? There’s event delegate: Listen for ancestors, wait until bubbles up, and decide if the element I clicked on is the one I want to listen for
So the advantage of using event delegate is that you can save memory by eliminating the number of listeners
3.3 Code Implementation
Need: listen for all li tags, console.log if the user clicked on li (‘ user clicked on li ‘)
<ul id="test">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
Copy the code
Implementation of the JS code
// Listen on the parent element ul#test
test.addEventListener('click'.(e) = > {
// Find the current clicked element using the e argument passed in from the browser
const t = e.target
// Check whether the current element is a Li tag
if(t.matches('li') {
console.log('User clicked on Li')}})Copy the code
The implementation idea is simple
- First listen for the parent element,
- And then based on the event information that the browser sent in, get the current clicked element,
- Check if the currently clicked element is li. If so, console.log(‘ user clicked the li tag ‘)
Based on this, we can encapsulate an event delegate function
on('click'.'#test'.'li'.() = >{
console.log('User clicked on Li')})function on(eventType, parentElement, selector, fn) {
// Check if it is element,
// If you pass in a selector other than element itself, change it to Element first.
// Because only Element can listen for events
if(! (parentElementinstanceof Element)) {
parentElement = parentElement.querySelectorAll(parentElement)
}
parentElement.addEventListener(eventType, (e) = >{
let target = e.target
if (target.matches(selector)) {
fn(e)
}
})
}
Copy the code
But there is a small problem with this implementation. What if the clicked element has more than one parent?
<ul id="test">
<li>
<p>
<span>1</span>
</p>
</li>
<li>
<p>
<span>2</span>
</p>
</li>
<li>
<p>
<span>3</span>
</p>
</li>
<li>
<p>
<span>4</span>
</p>
</li>
</ul>
Copy the code
All we need to do is recursively look up several levels of the parent node until we find the Li tag. We must also limit our search to no more than parentElement, such as the ul tag, for the body tag
on('click'.'#test'.'li'.() = >{
console.log('User clicked on Li')})function on(eventType, element, selector, fn) {
if(! (elementinstanceof Element)) {
element = document.querySelectorAll(element)
}
element.addEventListener(eventType, (e) = >{
let target = e.target
// Breaks out of the loop if a selector matches
while(! target.matches(selector)){if (target === element){
// If the parent element is found, set it to null
target = null
break
}
target = target.parentNode
}
// Call the function when target is found
target && fn.call(target, e)
})
}
Copy the code
4 summarizes
4.1 Summary capture and bubbling
- Capture: When the user clicks a button, the browser traverses the window from top to bottom, triggering event handlers one by one.
- Bubbling: The browser traverses the window from the bottom up to the button clicked by the user, triggering event handlers one by one.
4.2 What is Event Delegation
Listen for an ancestor element, thus listening for one and operating on multiple descendants at the same time
- Because of the bubble phase, the browser iterates from the bottom up to the window, triggering event handlers one by one,
- Therefore, it is possible to listen on an ancestor node (such as a parent node or a grandparent node) to process events of multiple children at the same time