文章目录
- [一、DOM 事件流](#一、DOM 事件流)
-
- [1、DOM 事件流简介](#1、DOM 事件流简介)
- [2、捕获阶段 Capture Phase](#2、捕获阶段 Capture Phase)
- [3、目标阶段 Target Phase](#3、目标阶段 Target Phase)
- [4、冒泡阶段 Bubbling Phase](#4、冒泡阶段 Bubbling Phase)
- [5、DOM 事件流案例说明](#5、DOM 事件流案例说明)
- 二、代码示例
-
- [1、捕获阶段 事件处理 代码示例](#1、捕获阶段 事件处理 代码示例)
-
- [① 代码示例](#① 代码示例)
- [② 执行结果](#② 执行结果)
- [2、冒泡阶段 事件处理 代码示例](#2、冒泡阶段 事件处理 代码示例)
-
- [① 代码示例](#① 代码示例)
- [② 执行结果](#② 执行结果)
一、DOM 事件流
1、DOM 事件流简介
DOM 事件流 描述了 在页面中 发生某个事件 时 , 事件如何 在 DOM 树中的 元素之间 传播的过程 ;
DOM 事件流分为 3 个阶段 :
- 捕获阶段 ( Capture Phase ) : 事件从 最顶层的父元素 document 开始 , 沿着 DOM 树向下传播到 目标元素 的 父元素 ;
- 核心作用 : 可以令 上层元素 提前拦截、处理事件 ;
- 关键特点 : 事件自上而下传播 , 需在 addEventListener 方法中 显式注册 , 第三参数设置 true ;
- 目标阶段 ( Target Phase ) : 事件到达 并 触发在目标元素本身 ;
- 核心作用 : 触发 目标元素 自身的 事件处理 ;
- 关键特点 : 事件的最终目的地 , 按注册顺序执行处理函数 ;
- 冒泡阶段 ( Bubbling Phase ) : 事件 从目标元素开始 , 沿着 DOM 树向上传播回最顶层的父元素 document ;
- 核心作用 : 令祖先元素 共享 事件处理机会 ;
- 关键特点 : 自下而上传播 , 支持事件委托 ;

2、捕获阶段 Capture Phase
捕获阶段 中 事件 从 最顶层的父元素 document 开始 , 沿着 DOM 树向下传播到 目标元素 的 父元素 ;
- 传播方向 : 顶层 document 元素 -> html 元素 -> body 元素 -> ... -> 父元素 -> 目标元素的父元素 -> 目标元素 ;
- 阶段目的 : 在事件到达 目标元素 之前 , 给上层元素一个机会来 捕获 和 处理 它 ;
- 触发操作 : 默认情况下 , 事件处理程序 不会在这个阶段触发 , 你需要通过 特定的方式 来注册捕获阶段的事件处理程序 , 如 : 设置 addEventListener 的 第三个参数 可以在 捕获阶段 触发事件回调函数 ;
3、目标阶段 Target Phase
目标阶段 事件 到达 并 触发 在目标元素本身 ;
- 传播方向 : 事件 直接作用于 触发事件 的元素 , 如 : 点击的按钮、输入的输入框 ;
- 核心目的 : 执行 目标元素 上绑定的所有 事件处理程序 , 这是事件的 " 最终目的地 " ;
- 多阶段注册 : 如果一个元素上 同时注册了 捕获阶段 和 冒泡阶段 的处理程序 , 它们会在这个阶段 按照 注册顺序 依次执行 , 先注册的先执行 ;
4、冒泡阶段 Bubbling Phase
冒泡阶段 Bubbling Phase 中 事件 从目标元素开始 , 沿着 DOM 树向上传播回最顶层的 document 元素 ;
- 传播方向 : 目标元素 -> 父元素 -> ... -> body 元素 -> html 元素 -> 顶层 document 元素 ;
- 核心目的 : 让目标元素的所有祖先元素都有机会处理这个事件 , 这是事件委托的核心原理 ;
- 注意点 : 大多数事件 ( click、input、mouseover ) 都有冒泡阶段 , 少数事件 ( focus、blur、load、unload ) 没有冒泡阶段 ;
5、DOM 事件流案例说明
下面的 DOM 事件流 是 为某个 <div> 标签元素 注册点击事件 并 执行 的过程分析 ;

事件捕获阶段 : 事件 从最顶层的 document 开始 , 自上而下依次传播到 html、body、div 等元素 , 即从祖先元素向目标元素的父元素传播 ;
该阶段让上层元素有机会提前拦截、处理事件 , 比如一些全局的事件控制可以在这个阶段实现 ;
事件目标阶段 : 当 事件传播到 最具体 的 目标元素(图中 "处于目标阶段" 对应的元素,通常是触发事件的具体节点,如这里的 div 所关联的实际元素)时,进入目标阶段 ;
该阶段的作用是 执行目标元素自身的事件处理程序 , 这是事件的 最终作用点 ;
事件冒泡阶段 : 事件 从目标元素开始 , 自下而上 依次 传播回 body、html、document , 即 从目标元素向祖先元素反向传播 ;
该阶段的作用是 让 目标元素 的 所有祖先元素 都有机会处理事件 , 这是事件委托的核心原理 , 只需给父元素绑定事件 , 就能处理子元素的事件 ;
二、代码示例
1、捕获阶段 事件处理 代码示例
① 代码示例
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM 事件流</title>
<style>
.father {
/*
父子元素的 margin 折叠 :
子元素的 margin-top/margin-bottom 可能会 "穿透" 父元素,
与父元素的 margin 合并 , 导致子元素的垂直 margin 失效
该 overflow: hidden 属性 可以阻止内部元素与外部元素的 margin 折叠
*/
overflow: hidden;
width: 200px;
height: 200px;
margin: 50px auto;
background-color: green;
}
.son {
width: 150px;
height: 150px;
margin: 25px;
background-color: red;
line-height: 150px;
color: #fff;
text-align: center;
}
</style>
</head>
<body>
<div class="father">
<div class="son">目标元素</div>
</div>
<script>
// 捕获阶段 事件处理
// 调用 addEventListener 方法 设置第三个参数是 true
// 捕获阶段事件传递顺序 document -> html -> body -> father -> son
var son = document.querySelector('.son');
son.addEventListener('click', function() {
alert('son');
}, true);
var father = document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, true);
document.addEventListener('click', function() {
alert('document');
}, true)
</script>
</body>
</html>
② 执行结果
执行后 , 点击子元素 , 捕获阶段 事件触发顺序 : 顶层 document 元素 -> 父元素 -> 子元素 ;

2、冒泡阶段 事件处理 代码示例
① 代码示例
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM 事件流</title>
<style>
.father {
/*
父子元素的 margin 折叠 :
子元素的 margin-top/margin-bottom 可能会 "穿透" 父元素,
与父元素的 margin 合并 , 导致子元素的垂直 margin 失效
该 overflow: hidden 属性 可以阻止内部元素与外部元素的 margin 折叠
*/
overflow: hidden;
width: 200px;
height: 200px;
margin: 50px auto;
background-color: green;
}
.son {
width: 150px;
height: 150px;
margin: 25px;
background-color: red;
line-height: 150px;
color: #fff;
text-align: center;
}
</style>
</head>
<body>
<div class="father">
<div class="son">目标元素</div>
</div>
<script>
// 冒泡阶段 事件处理
// 调用 addEventListener 方法 设置第三个参数是 false , 如果不设置默认也是 false
// 冒泡阶段事件传递顺序 son -> father ->body -> html -> document
var son = document.querySelector('.son');
son.addEventListener('click', function() {
alert('son');
}, false);
var father = document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, false);
document.addEventListener('click', function() {
alert('document');
})
</script>
</body>
</html>
② 执行结果
执行后 , 点击子元素 , 冒泡阶段 事件触发顺序 : 子元素 -> 父元素 -> 顶层 document 元素 ;
