浏览器-事件流的总结和理解(捕获阶段 目标阶段 冒泡阶段解析)
一 DOM事件流
百度百科,对于DOM事件流的解释:DOM(文档对象模型)结构是一个树型结构,当一个HTML元素产生一个事件时,该事件会在元素节点与根结点之间的路径传播,路径所经过的结点都会收到该事件,这个传播过程可称为DOM事件流。
当有事件触发时,从DOM的根节点开始,向内传递,这一过程可以理解成①②③④的捕获阶段;
如果下图代码中的d节点被触发,事件整个传递到达目标阶段,该阶段结束后,事件会向上传递,一过程按照水底的气泡一样向上漂 ,称之为的⑤⑥⑦⑧ 冒泡阶段;
javaScript事件的三个阶段:捕获阶段 目标阶段 冒泡阶段
捕获阶段 概念:
事件从根节点流向目标节点,途中流经各个DOM节点,在各个节点上触发捕获事件,直到达到目标节点。
目标阶段 概念:
事件到达目标节点时,就到了目标阶段,事件在目标节点上被触发
冒泡阶段 概念:
事件在目标节点上触发后,不会终止,一层层向上冒,回溯到根节点
<div id="a"> <div id="b"> <div id="c"> <div id="d">点击这里</div> </div> </div> </div>
二 捕获事件流
捕获事件流可以在支持W3C 标准的浏览器中使用addEventListener处理,在菜鸟教程:addEventListener 中在线体验.
希望注册在DOM元素上的事件处理程序在捕获阶段还是在冒泡阶段触发,取决于 addEventListener() 方法的第三个参数为 true 还是 false ,addEventListener的第三个参数是一个布尔类型
1、第三个参数是false:事件从里到外执行,这种效果叫事件冒泡
2、第三个参数是true:事件从里到外执行,执行顺序颠倒过来了,这种效果叫做事件捕获。
第一阶段:捕获阶段
第二阶段:目标阶段(执行当前点击的元素)
第三阶段:冒泡阶段
该函数有三个参数:
- 事件名
- 触发执行函数
- 冒泡阶段还是捕获阶段执行,默认是在冒泡阶段执行
<div id="a"> <div id="b"> <div id="c"> <div id="d">点击这里</div> </div> </div> </div>
const a = document.getElementById('a') const b = document.getElementById('b') const c = document.getElementById('c') const d = document.getElementById('d') a.addEventListener('click', (e) => { const { target, currentTarget } = e console.log(`target是${target.id}`) console.log(`currentTarget是${currentTarget.id}`) }) b.addEventListener('click', (e) => { const { target, currentTarget } = e console.log(`target是${target.id}`) console.log(`currentTarget是${currentTarget.id}`) }) c.addEventListener('click', (e) => { const { target, currentTarget } = e console.log(`target是${target.id}`) console.log(`currentTarget是${currentTarget.id}`) }) d.addEventListener('click', (e) => { const { target, currentTarget } = e console.log(`target是${target.id}`) console.log(`currentTarget是${currentTarget.id}`) })
第三个参数不设置默认冒泡顺序,当触发d时呈现的结果如下
target是d currentTarget是d
target是d currentTarget是c
target是d currentTarget是b
target是d currentTarget是a
const a = document.getElementById('a') const b = document.getElementById('b') const c = document.getElementById('c') const d = document.getElementById('d') a.addEventListener('click', (e) => { const { target, currentTarget } = e console.log(`target是${target.id}`) console.log(`currentTarget是${currentTarget.id}`) },true) b.addEventListener('click', (e) => { const { target, currentTarget } = e console.log(`target是${target.id}`) console.log(`currentTarget是${currentTarget.id}`) },true) c.addEventListener('click', (e) => { const { target, currentTarget } = e console.log(`target是${target.id}`) console.log(`currentTarget是${currentTarget.id}`) },true) d.addEventListener('click', (e) => { const { target, currentTarget } = e console.log(`target是${target.id}`) console.log(`currentTarget是${currentTarget.id}`) },true)
将addEventListener第三个参数设置为true,看看捕获阶段呢?
target是d currentTarget是a
target是d currentTarget是b
target是d currentTarget是c
target是d currentTarget是d
总结如下:
e.target
:触发事件的元素e.currentTarget
:绑定事件的元素
三 事件委托
通过上面的例子,我们可以很清晰的发现,如果当前父级元素下有若干个子级元素,对父级做监听后,当我们通过addEventListener回调函数返回的e.tartget
便能得到触发事件的元素。
<div id='list'> <div id='item-1'>item-1</div> <div id='item-2'>item-2</div> <div id='item-3'>item-3</div> <div id='item-4'>item-4</div> </div>
通过给id为list的元素添加监听,在点击列表中的元素,便可实现事件委托,事件委托便是处理事件处理程序过多问题的一种解决方案;
四 阻止事件流
阻止事件流常用的两种种方式
e.stopPropagation():阻止目标元素的事件冒泡到父级元素;
e.preventDefault():取消一个目标元素的默认行为(例如:a链接,input标签的submit等);