JS事件捕获与冒泡

236 阅读1分钟

事件模型

一、DOM标准规定,事件发生的过程,要遵循事件模型: 3个阶段

  1. 捕获: 从根节点document开始,由外向内,依次遍历实际点击的元素的各级父元素。并记录各级父元素上是否绑定了单击事件。——只记录,不执行
  2. 目标触发:优先触发实际点击的目标元素上绑定的事件处理函数 目标元素(target): 实际想点击的那个元素
  3. 冒泡: 由内向外,按捕获阶段的反向,依次触发各级父元素上的事件处理函数。

二、事件的冒泡

如图,点击最内层的块时,会触发外层所有的click事件

Snipaste_2021-09-18_23-58-31.png

事件触发顺序是由内到外的,这就是事件冒泡。如果点击子元素不想触发父元素的事件,可使用event.stopPropagation()方法

Snipaste_2021-09-19_00-06-15.png

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .d1 .d2 .d3 {
        cursor: pointer;
      }
      .d1 {
        width: 300px;
        height: 300px;
        background: red;
        position: relative;
      }
      .d2 {
        width: 200px;
        height: 200px;
        background: green;
        position: absolute;
        top: 50px;
        left: 50px;
      }
      .d3 {
        width: 100px;
        height: 100px;
        background: blue;
        position: absolute;
        top: 50px;
        left: 50px;
      }
    </style>
  </head>
  <body>
    <div id="root">
      <div class="d1">
        <div class="d2">
          <div class="d3"></div>
        </div>
      </div>
    </div>
  </body>
  <script>
    let d1 = document.getElementsByClassName("d1")[0];
    let d2 = document.getElementsByClassName("d2")[0];
    let d3 = document.getElementsByClassName("d3")[0];
    //绑定事件
    d1.onclick = function (e) {
      e.stopPropagation();
      console.log("点到D1了!");
    };
    d2.onclick = function (e) {
      e.stopPropagation();
      console.log("点到D2了!");
    };
    d3.onclick = function (e) {
      e.stopPropagation();
      console.log("点到D3了!");
    };
  </script>
</html>
复制代码

三、事件的捕获

还是上面的图,这次用addEventListener(event, function, useCapture)来校验

Snipaste_2021-09-19_00-34-04.png

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .d1 .d2 .d3 {
        cursor: pointer;
      }
      .d1 {
        width: 300px;
        height: 300px;
        background: red;
        position: relative;
      }
      .d2 {
        width: 200px;
        height: 200px;
        background: green;
        position: absolute;
        top: 50px;
        left: 50px;
      }
      .d3 {
        width: 100px;
        height: 100px;
        background: blue;
        position: absolute;
        top: 50px;
        left: 50px;
      }
    </style>
  </head>
  <body>
    <div id="root">
      <div class="d1">
        1
        <div class="d2">
          2
          <div class="d3">3</div>
        </div>
      </div>
    </div>
  </body>
  <script>
    let d1 = document.getElementsByClassName("d1")[0];
    let d2 = document.getElementsByClassName("d2")[0];
    let d3 = document.getElementsByClassName("d3")[0];
    //绑定事件
    //addEventListener(event, function, useCapture)
    //addEventListener(绑定的事件, 执行的函数, '参数默认值是false,表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数')
    d1.addEventListener(
      "click",
      function () {
        console.log("d1被点击了!");
      },
      true
    );

    d2.addEventListener(
      "click",
      function (e) {
        console.log("d2被点击了!");
      },
      true
    );
    d3.addEventListener(
      "click",
      function () {
        console.log("d3被点击了!");
      },
      true
    );
  </script>
</html>

复制代码

当点击最内层的块的时候,我们可以看到控制台打印的顺序是由外到内 和事件的冒泡顺序恰恰相反

四、事件委托(事件代理)

说白了就是相同的事件,绑定在最外层的元素上。让最外层的元素,帮内层的元素触发事件,也就是事件的代理(委托)。然后可以通过event.target去获取到事件源,能够减少监听事件,提升性能。

Snipaste_2021-09-19_00-45-00.png 如图 只给最外层的ul绑定了事件,但是点击星期三时,就能触发事件,打印我们想要的结果。

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <ul class="day">
      <li>星期一</li>
      <li>星期二</li>
      <li>星期三</li>
      <li>星期四</li>
      <li>星期五</li>
    </ul>
  </body>
  <script>
    let day = document.getElementsByClassName("day")[0];

    day.onclick = function (e) {
      console.log(e.target.innerText);
    };
  </script>
</html>