【每日一题NO.55】多个DOM事件先捕获还是先冒泡?

前端印记

共 2476字,需浏览 5分钟

 ·

2021-10-12 11:56

人生苦短,总需要一点仪式感。比如学前端~

问题

在一个 DOM 上同时绑定两个点击事件:一个用捕获,一个用冒泡。
事件会执行几次?
先执行的是冒泡还是捕获?

前置知识

关于DOM事件流的相关基础知识,可以看上一篇昨天的每日一题:【每日一题NO.54】说下你对时间流的理解

冒泡和捕获的触发顺序

冒泡机制的触发顺序是从下往上的,当 DOM 元素绑定的事件被触发时,此时该元素为目标元素,目标元素执行后,它的父级至祖先级元素绑定的事件会向上依次执行。

(图源:高程4)

捕获机制和冒泡相反,目标元素被触发后,事件会从目标元素所在层级树的最顶层祖先元素往下执行到目标元素为止。

(图源:高程4)

关于执行次数:

首先需要明确的是,使用DOM0级事件处理程序给一个元素绑定多个事件,也只会执行最后一个事件。
使用DOM2级事件处理程序addEventListener就可以绑定多个,且绑定了几个事件就会执行几次。

addEventListener

addEventListener 函数的第三个参数设置为 false,说明为该元素绑定的事件使用冒泡机制;为true则表示使用捕获机制。

大多数情况下,事件处理程序会被添加到事件流的冒泡阶段,主要原因是跨浏览器兼容性好。把事件处理程序注册到捕获阶段通常用于在事件到达其指定目标之前拦截事件。如果不需要拦截,则不要使用事件捕获。

关于多个事件的执行顺序

所有事件的顺序是:其它元素捕获阶段事件---到达目标元素阶段事件---其他元素冒泡阶段事件

当一个元素绑定了两个事件,一个是冒泡,一个是捕获。
该 DOM 上的事件如果被触发,执行顺序会按照下边的规则执行:

  • 如果该 DOM 是目标元素,则按事件绑定顺序执行,不区分冒泡还是捕获【新版浏览器实验显示:目标元素事件区分冒泡和捕获,执行顺序都是先捕获、后冒泡,无视目标元素事件绑定顺序】
  • 如果该 DOM 是处于事件流中的非目标元素,则先执行捕获后执行冒泡。因为 W3C 标准有说明,先发生捕获事件,后发生冒泡事件

实验代码定义了一组元素,嵌套层级如下:


点击中央编号为9的元素,所有该元素层级树上的元素事件触发顺序如下:


可以看到,顺序是按照先从顶级元素向下捕获到目标元素,到目标元素事件按照先捕获后冒泡,最后再从目标元素冒泡至根元素。

DOM2 Events 规范规定事件流分为 3 个阶段:事件捕获、到达目标和事件冒泡。事件捕获最先发生, 为提前拦截事件提供了可能。然后,实际的目标元素接收到事件。最后一个阶段是冒泡,最迟要在这个 阶段响应事件。

需要注意的是:在冒泡阶段,向上执行的过程中,已经执行的捕获事件不再执行,只执行冒泡事件。比如上边蓝色字的layer8,第1、3次的捕获事件执行过后,冒泡至layer8后不再执行捕获事件。


[引用]

  • Javascript高级程序设计第四版 - 17.1.3 DOM事件流

[实验相关代码](主要是看js事件的定义

<div id="layer7" data-cont="7">
  <div id="layer8" data-cont="8">
    <div id="layer9" data-cont="9">
      随机
    div>
  div>
div>
layer7.addEventListener("click", (e) => {
  console.log('%clayer7 - click - 第 1 次绑定 - 冒泡''color: #f39900')
}, false)
layer7.addEventListener("click", (e) => {
  console.log('%clayer7 - click - 第 2 次绑定 - 捕获''color: #f39900')
}, true)
layer7.addEventListener("click", (e) => {
  console.log('%clayer7 - click - 第 3 次绑定 - 冒泡''color: #f39900')
}, false)
layer8.addEventListener("click", (e) => {
  console.log('%clayer8 - click - 第 1 次绑定 - 捕获''color: blue')
}, true)
layer8.addEventListener("click", (e) => {
  console.log('%clayer8 - click - 第 2 次绑定 - 冒泡''color: blue')
}, false)
layer8.addEventListener("click", (e) => {
  console.log('%clayer8 - click - 第 3 次绑定 - 捕获''color: blue')
}, true)
layer9.addEventListener("click", (e) => {
  console.log('%clayer9 - click - 第 1 次绑定 - 捕获''color: red')
}, true)
layer9.addEventListener("click", (e) => {
  console.log('%clayer9 - click - 第 2 次绑定 - 冒泡''color: red')
}, false)
layer9.addEventListener("click", (e) => {
  console.log('%clayer9 - click - 第 3 次绑定 - 捕获''color: red')
}, true)


所有《每日一题》的 知识大纲索引脑图 整理在此:https://www.yuque.com/dfe_evernote/interview/everyday
你也可以点击文末的 “阅读原文” 快速跳转


END
愿你历尽千帆,归来仍是少年。
浏览 21
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报