likes
comments
collection
share

重学精进 DOM0 、 DOM2、DOM3 级事件

作者站长头像
站长
· 阅读数 13

DOM0 级事件

优点

  • 兼容性好
  • 移除事件简单
  • 节点上 onclick 属性被 Node.cloneNode 克隆,通过 JS 赋值的 onclick 不可以

注意

  • 绑定的事件处理函数,函数里面的 this 指向绑定的节点
  • 相同事件只能定义一个回调函数
  • html 上绑定的函数,会在全局作用域查找
<body>
  <button onclick="console.log('按钮1被点击了')">按钮1(html代码)</button>
  <button onclick="onClick2()">按钮2(函数)</button>
  <button onclick="console.log(this.className, this);" class="button3">
    按钮3(this)
  </button>
  <button onclick="onClick4()" class="button3">按钮4(全局作用域)</button>

  <script>
    function onClick2() {
      console.log("按钮2被点击了");
    }

    (function () {
      function onClick4() {
        console.log("按钮4被点击了");
      }
    })();
  </script>
</body>

复制绑定事件

<body>
  <div>原始区域:</div>
  <div id="sourceButtons">
    <button class="btn1" onclick="console.log('按钮1被点击了')">按钮1(html代码)</button>
    <button class="btn2">按钮2(函数)</button>
  </div>

  <!-- 复制了绑定在按钮1的事件,但不会复制按钮2 -->
  <div id="copyZone">
    <div>复制区域:</div>
  </div>

  <script>
    document.querySelector(".btn2").onclick = onClick2;

    copyZone.append(sourceButtons.cloneNode(true));

    function onClick2() {
      console.log("按钮2被点击了");
    }
  </script>
</body>

DOM2 级的事件

重学精进 DOM0 、 DOM2、DOM3 级事件

注册

// useCapture:true,捕获阶段传播到目标的时候触发
// 反之冒泡阶段传到目标的时候触发。默认值 false,即冒泡时
target.addEventListener(type, listener, useCapture);
target.addEventListener(type, listener, options);
<body>
  <button id="btn">按钮</button>

  <script>
    window.addEventListener(
      "click",
      () => {
        console.log("捕获:window");
      },
      true
    );

    document.addEventListener(
      "click",
      () => {
        console.log("捕获:document");
      },
      true
    );

    btn.addEventListener(
      "click",
      e => {
        console.log("捕获:btn");
      },
      true
    );

    btn.addEventListener("click", e => {
      console.log("冒泡:btn");
    });

    document.addEventListener("click", () => {
      console.log("冒泡:document");
    });

    window.addEventListener("click", () => {
      console.log("冒泡:window");
    });
  </script>
</body>

打印结果:

重学精进 DOM0 、 DOM2、DOM3 级事件


once

  • 是否只响应一次
  • 最典型的应用可能就是现代浏览器视频播放,需要用户点击一次后播放
<body>
  <video id="video" controls width="250" autoplay>
    <source
      src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm"
      type="video/webm"
    />
    <source
      src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
      type="video/mp4"
    />
    Sorry, your browser doesn't support embedded videos.
  </video>

  <script>
    document.addEventListener(
      "click",
      () => {
        console.log("播放视频");
        video.play();
      },
      {
        once: true
      }
    );
  </script>
</body>

passive

  • 设置为 true 时,事件处理不会调用 preventDefault(),可提高性能
ele.addEventListener("touchmove", ()=>{}, {
  passive: true,
})

signal

  • AbortSignal,该 AbortSignal 的 abort() 方法被调用时,监听器会被移除
  • AbortSignal 也被用于取消 fetch 请求
<body>
  <button id="btn">按钮</button>

  <script>
    const controller = new AbortController();

    const signal = controller.signal;

    btn.addEventListener(
      "click",
      () => {
        console.log("按钮被点击了");
        controller.abort();
      },
      { signal }
    );
  </script>
</body>

e.preventDefault

  • 阻止默认的行为,比如有 href 属性的 a 标签不会跳转,checkbox 的选中不会生效等
  • 事件依旧还会继续传递
<body>
  <a id="aLink" target="_blank" href="https://www.imooc.com/">跳转到xxx</a>
  <input id="cBox" type="checkbox" /><label for="ckBox">复选框</label>

  <script>
    aLink.addEventListener("click", e => {
      e.preventDefault();
    });

    cBox.addEventListener("click", e => {
      e.preventDefault();
    });
  </script>
</body>

e.stopPropagation

  • 阻止捕获和冒泡阶段中当前事件的进一步传播
<body>
  <button id="btn">按钮</button>

  <script>
    btn.addEventListener("click", () => {
      console.log("冒泡:btn");
    });

    document.addEventListener("click", () => {
      console.log("冒泡:document");
    });

    btn.addEventListener(
      "click",
      () => {
        console.log("捕获:btn");
      },
      true
    );

    document.addEventListener(
      "click",
      e => {
        console.log("捕获:document");
        e.stopPropagation();
      },
      true
    );
  </script>
</body>

打印结果:

捕获:document

e.stoplmmediatePropagation

  • 阻止监听同一事件的其他事件监听器被调用
<body>
  <button id="btn">按钮</button>

  <script>
    btn.addEventListener("click", () => {
      console.log("冒泡:btn");
    });

    btn.addEventListener(
      "click",
      () => {
        console.log("捕获:btn 1");
      },
      true
    );

    btn.addEventListener(
      "click",
      e => {
        // 阻止 click 事件冒泡,并且阻止 btn 元素上绑定的其他 click 事件的事件监听函数的执行
        e.stopImmediatePropagation();
        console.log("捕获:btn 2");
      },
      true
    );

    btn.addEventListener(
      "click",
      () => {
        console.log("捕获:btn 3");
      },
      true
    );

    document.addEventListener("click", () => {
      console.log("冒泡:document");
    });

    document.addEventListener(
      "click",
      () => {
        console.log("捕获:document");
      },
      true
    );
  </script>
</body>

执行结果如下:

捕获:document
捕获:btn 1
捕获:btn 2

useCapture

  • 如果这个参数相同并且事件回调函数相同,事件不会被添加
<body>
  <button id="btn">按钮</button>

  <script>
    function onClick() {
      // 只打印两次
      console.log("按钮被点击了");
    }
    // capture选项都是false, 只有一个添加成功
    btn.addEventListener("click", onClick);
    btn.addEventListener("click", onClick);

    // capture选项都是true, 只有一个添加成功
    btn.addEventListener("click", onClick, {
      capture: true
    });
    btn.addEventListener("click", onClick, {
      capture: true
    });
  </script>
</body>

target 和 currentTarget

  • target: 触发事件的元素,谁触发就是谁
  • currentTarget: 事件绑定的元素,谁添加的事件函数就是谁
<body>
  <button id="btn">按钮</button>

  <script>
    // 点击btn的时候
    document.addEventListener("click", e => {
      console.log(e.target); // button元素
      console.log(e.currentTarget); // document元素
    });
  </script>
</body>

事件委托

  • 基于事件冒泡的机制,利用外层节点处理事件
  • 优点:不能为每个元素单独绑定事件函数、减少内存消耗
<body>
  <ul id="ulList">
    <li>
      <a class="btn-buy" data-id="1">白菜</a>
    </li>
    <li>
      <a class="btn-buy" data-id="2">萝卜</a>
    </li>
  </ul>

  <script>
    // 绑定
    ulList.addEventListener("click", e => {
      // 识别节点
      if (e.target.classList.contains("btn-buy")) {
        console.log("商品id:", e.target.dataset.id);
      }
    });
  </script>
</body>

DOM3 级事件

  • DOM3 Events 在 DOM2 Events 基础上重新定义了事件,并增加了新的事件类型

重学精进 DOM0 、 DOM2、DOM3 级事件


注意事项和建议

  • DOMO 级事件一定程度上可以复制,DOM2级别事件不可以复制
  • 合理利用选项 once 和 passive 和 事件代理 提升性能
  • capture 选项相同和并且事件回调函数相同,事件不会被添加
  • 因为都是继承于 EventTarget,任何一个节点都是事件中心