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 phaseonclickEvent 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 phaseOnclickEvent, if so, run
  • Then, look for the next oneparentNode, (in the initial example, the son div)
  • And then see if there are any in the bubbling phaseOnclickEvent, 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

  1. First listen for the parent element,
  2. And then based on the event information that the browser sent in, get the current clicked element,
  3. 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