详解JS事件流

328 阅读3分钟

之前对JS的事件流没有认真的去研究过,现在想好好记录下JS事件流的执行顺序。在介绍事件流之前先介绍一下DOM事件的分类。

DOM事件的分类

DOM事件分为:

  • DOM0级事件
  • DOM2级事件

DOM0级事件的注册:

// 直接在HTML标签中注册
<div onclick="clickHandler"></div>
<script>
  function clickHandler(e) {
      console.log('我是DOM0级事件');
  }
</script>
// 在JS中直接定义在dom对象上
<div id="js-opera"></div>
<script>
  const dom = document.querySelector('#js-opera');
  dom.onclick = function(e) {
      console.log('我是DOM0级事件');
  }
</script>

DOM2级事件的注册

<div id="js-opera"></div>
<script>
   // 通过addEventListener注册
   const dom = document.querySelector('#js-opera');
   function clickHanlder() {
       console.log('我是DOM2级事件');
   }
   dom.addEventListener('click', clickHanlder);
</script>

事件流

事件流有两种形式:

  • 捕获: 从上到下
  • 冒泡: 从下到上

执行顺序

事件的执行顺序是:捕获阶段 -> 目标阶段 -> 冒泡阶段;如下所示:

js事件流

那么什么样的事件是捕获事件,什么样的事件是冒泡事件呢?还记得开始说的事件分类吗?对于DOM0级事件我们可以把它认为是在冒泡阶段执行的,对于DOM2级事件(addEventListener)定义时有3个参数:

  • 第一个参数:事件名,如click、mousemove
  • 第二个参数:回调函数
  • 第三个参数:事件流类型,true | false,默认为false
    • true: 代表捕获事件
    • false: 代表冒泡事件

所以事件的执行顺序是先从上执行父级的捕获事件(如果有的话),所有父级的捕获事件执行完之后在执行触发事件的dom元素绑定的相关事件,最后在从目标元素从下往上执行冒泡事件。

目标阶段事件执行的顺序

捕获阶段和冒泡阶段事件的执行顺序很好理解,那么对于目标阶段呢?目标阶段的dom元素可能定义了捕获事件、冒泡事件|DOM0级事件。对于目标阶段执行事件的顺序是不管冒泡还是捕获的,执行顺序是根据事件在js中定义的顺序来的。

实践

接下来我们来举例验证一下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>事件流</title>
    <style>
      .fd-deep-1 {
          text-align: center;
          width: 200px;
          height: 100px;
          border: 1px solid #cecece;
          margin: 0 auto;
          cursor: pointer;
      }
      .fd-deep-2 {
          text-align: center;
          width: 150px;
          height: 75px;
          border: 1px solid #cecece;
          margin: 0 auto;
          cursor: pointer;
      }
      .fd-deep-3 {
          text-align: center;
          width: 100px;
          height: 50px;
          border: 1px solid #cecece;
          margin: 0 auto;
          cursor: pointer;
      }
    </style>
</head>
<body>
    <div class="fd-deep-1" onclick="clickHandler('deep-1')">
        deep-1
        <div class="fd-deep-2" onclick="clickHandler('deep-2')">
            deep-2
            <div class="fd-deep-3" onclick="clickHandler('deep-3')">deep-3</div>
        </div>
    </div>
    <script>
       function clickHandler(param) {
           console.log(param);
       }
       function getDom(className) {
           return document.querySelector(`.${className}`);
       }
       const deep1 = getDom('fd-deep-1');
       const deep2 = getDom('fd-deep-2');
       const deep3 = getDom('fd-deep-3');
       deep1.addEventListener('click', function() {console.log('deep1冒泡')});
       deep1.addEventListener('click', function() {console.log('deep1捕获')}, true);
       deep2.addEventListener('click', function() {console.log('deep2冒泡')});
       deep2.addEventListener('click', function() {console.log('deep2捕获')}, true);
       deep3.addEventListener('click', function() {console.log('deep3冒泡')});
       deep3.addEventListener('click', function() {console.log('deep3捕获')}, true);
    </script>
</body>
</html>

输出结果为:

deep1捕获
deep2捕获
deep-3
deep3冒泡
deep3捕获
deep-2
deep2冒泡
deep-1
deep1冒泡