I. 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?
Question 2:
- Click on the text to call fn1, fn2, or fn3 first?
- The answer is either, if it’s a capture mechanism, call FN1 from the outside in first
- If it’s bubbling, it works from the inside out, that is, calling FN3 first
Let’s take a closer look at DOM event mechanisms and event delegates
DOM event mechanism
The DOM event mechanism has two main stages, which are:
- Capture phase
- 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 if the outermost ancestor of the element registered an onclick event handler during the capture phase, and if so, runs it.
- It then moves to click the next ancestor of the element in, and performs the same operation, then click the element and the next ancestor, and so on until it reaches the actual clicked element.
In the bubbling stage:
- The browser first checks the clicked element (in the initial example, text), then sees if there is an Onclick event in the bubble phase, and if so, runs it
- Then, look for the next parentNode (in the initial example, the son div) and see if there is an Onclick event in the bubble phase, if so, run it
- Then find the next ParentNode (in our example, the dad div).
- When we use addEventListener to listen for events, addEventListener(‘click’, fn, bool)
- If the third parameter bool is not passed, or false is passed, then fn is called during the bubble phase
- If the third parameter Bool is passed true, then fn is called during the capture phase
2.2 Cancel bubbling
- Capture cannot be cancelled, but bubbling can, as e.propagation() does
- However, some events cannot be cancelled, such as scroll event, which can be queried on MDN
2.3 Differences between Target and currentTarget
- E.target The element that the user is working on
- E.currenttarget The element that the programmer is listening on
For example:
<div>
<span>The text</span>
</div>
Copy the code
Let’s say we’re listening for div, but the user is actually clicking on text, then
- E.target is the SPAN tag
- E.currenttarget is the div tag
2.4 Summary Capture and bubble: Capture: When the user clicks a button, the browser will traverse the window from top to bottom to the button clicked by the user, 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
Scene 1:
We want to add click events to 100 buttons, what do we do? 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
Scene 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 delegates is that
- You can save memory by eliminating the number of listeners
- You can listen for dynamic elements
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
So the JS code implemented is:
// 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
What we need to do is:
- I recursively go up several levels of the parent node until I find the LI tag,
- You must also limit the search to no more than parentElement,
- In the example above, you should not go beyond the UL tag to look 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
Four,
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
Because of the bubble phase, the browser iterates from the user’s click from bottom up to the window, triggering event handlers one by one, it can listen to one ancestor node (such as the parent node, the grandfather node) to process events of multiple children at the same time
This article is originally published by FJL. The copyright of this article belongs to me and Hungry People Valley