Dom's event model, event bubbling, preventing bubbling, and event delegation

Posted by mogster on Wed, 13 May 2020 20:54:38 +0200

What is the event model

Dom standard event model: when an event occurs, it actually goes through three stages!

  1. Capture stage from the outside to the inside: from the root node of Dom tree to the current clicked element position, traverse and record the event processing functions bound to the parent elements at all levels of the current element, this stage is just record and not trigger.
  2. Target trigger stage: priority to trigger the processing function of the current clicked element
  3. Bubbling execution stage: it reversely triggers events on all levels of parent elements from inside to outside according to the sequence of event processing functions on each parent element recorded in capture stage

As shown in the figure

Take an example

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <style>
    #d1 #d2 #d3 {
      cursor: pointer;
    }

    #d1 {
      background-color: green;
      position: relative;
      width: 150px;
      height: 150px;
      text-align: center;
      cursor: pointer;
    }

    #d2 {
      background-color: blue;
      position: absolute;
      top: 25px;
      left: 25px;
      width: 100px;
      height: 100px;
    }

    #d3 {
      background-color: red;
      position: absolute;
      top: 25px;
      left: 25px;
      width: 50px;
      height: 50px;
      line-height: 50px;
    }
  </style>
  <body>
    <div id="d1">
      1
      <div id="d2">
        2
        <div id="d3">3</div>
      </div>
    </div>
  </body>
</html>

Rendering of web page

If I bind a click event to each div, the content of the pop-up window will be painful if I click on it. The JavaScript code is as follows:

<script>
var d1=document.getElementById("d1");
var d2=document.getElementById("d2");
var d3=document.getElementById("d3");
      //Original meaning: point who, who cries to ache!
      d1.onclick=function(){
        alert("d1 they hurt!");
      }
      d2.onclick=function(){
        alert("d2 they hurt!");
      }
      d3.onclick=function(){
        alert("d3 they hurt!");
      }
</script>

The requirement is that the dot 1 green div should only show "d1 pain", the dot 2 Blue div should only show "d2 pain", and the dot 3 red div should only show "d3 pain".
But since the event model is bubbling, if you click 2 blue, you will see "d2 pain" and "d1 pain". This causes the event to bubble.

Stop bubbling / spreading

Use the stopPropagation() method in the event object to prevent bubbling / stop propagation. e.stopPropagation() prevents the execution of processing functions on the parent element, not the current element's own processing functions
So if you want to implement the pop-up window, the following code.

<script>
      var d1=document.getElementById("d1");
      var d2=document.getElementById("d2");
      var d3=document.getElementById("d3");
      //Original meaning: point who, who cries to ache!
      //Event when an event occurs
      //                  ↓
      d1.onclick=function(e){
        //e.stopPropagation() / / because there is no executable processing function outside d1
        alert("d1 they hurt!");
      }
      d2.onclick=function(e){
        //e.stopPropagation() / / all right
        alert("d2 they hurt!");
        e.stopPropagation() //All right
      }
      d3.onclick=function(e){
        e.stopPropagation()
        alert("d3 they hurt!");
      }
</script>

So that's what we need.
But is bubbling a good mechanism? If not, why does the event model default to bubbling? Now let's look at the role of bubbling

Event principal / agent

What is event delegation

If multiple level child elements are bound to the same event, they should only be bound to the parent element once. All child elements can share an event handler on the parent element through the bubbling mechanism!

Why use event delegation

Because browser triggered events are triggered by traversing the event listening queue to find the qualified listening objects. If there are many listening objects! Traversal will be slow, resulting in slow event response,

How to use event delegation? Let's use a simple accordion as an example

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <style>
    ul {
      list-style: none;
    }
    li:first-child {
      border-top-left-radius: 7px;
      border-top-right-radius: 7px;
    }
    li:nth-child(7) {
      border-bottom-left-radius: 7px;
      border-bottom-right-radius: 7px;
    }
    li {
      width: 200px;
      height: 50px;
      line-height: 50px;
      border: 1px solid #ddd;
      background-color: #fff;
    }
    .none {
      display: none;
    }
    .block {
      display: block;
    }
  </style>
  <body>
    <ul>
      <li>accordion1</li>
      <span class="none">accordion1Content of</span>
      <li>accordion2</li>
      <span class="none">accordion2Content of</span>
      <li>accordion3</li>
      <span class="none">accordion3Content of</span>
      <li>accordion4</li>
      <span class="none">accordion4Content of</span>
    </ul>

    <script>
      var ul = document.querySelector("ul");   //Get parent element
      //Bind a click event to the parent element
      ul.onclick = function (e) {
        //e.target specifically holds the special attributes of the target element that was originally clicked on. Once the original target element is saved, it will not change with bubbling!
        if (e.target.nodeName == "LI") {
          var span = e.target.nextElementSibling;
          var b_span = document.querySelector(".block");
          if (span.className == "block") {
            span.className = "none";
          } else {
            if (b_span != null) {
              b_span.className = "none";
              span.className = "block";
            }
            span.className = "block";
          }
        }
      };
    </script>
  </body>
</html>

The effect is as follows

Effect 1 Effect 2 Effect 3

Bind the same event processing function triggered by the child element to the parent element, obtain the trigger event function through event bubbling, and use e.target to point to the currently clicked element, so that the currently clicked element can trigger the event. This is the event delegation

Original article 9 praised 11 visited 1940
follow private letter

Topics: Javascript