捕获:当用户点击按钮,浏览器会从 window 从上向下遍历至用户点击的按钮,逐个触发事件处理函数。 冒泡:浏览器从用户点击的按钮从下往上遍历至 window,逐个触发事件处理函数。
W3C 事件模型/事件机制:对每个事件先捕获再冒泡
先从点击事件开始说起
<div class= 爷爷>
<div class= 爸爸>
<div class= 儿子>
文字
</div>
</div>
</div>
当给三个div分别添加事件监听 fnYe/fnBa/fnEr
问题:点击文字,最先调用那个函数?
IE5 认为先调用fnEr
网景认为先调用fnYe
W3C 发布标准:
规定浏览器应该同时支持两种调用顺序
首先按 爷爷=>爸爸=>儿子 顺序看有没有函数监听
然后按 儿子=>爸爸=>爷爷 顺序看有没有函数监听
有监听函数就调用,并提供事件信息,没有就跳过
像这种
从外向内找监听函数,叫事件捕获
从内向外找监听函数,叫事件冒泡
事件绑定API
如果 bool 不传或为 falsy
就让 fn 走冒泡,即当浏览器在冒泡阶段发现 baba 有 fn 监听函数,就会调用 fn,并提供事件信息
在事件结束后 e 会自动消亡,鼠标点击动作结束后 e 就消亡了
获取点击的状态,可以使用 currentTarget
level1.addEventListener('click', (e)=>{
const t = e.currentTarget
setTimeout(()=>{
t.classList.remove('x')
},n*1000)
在事件结束后,不推荐大家继续用 e,因为 e 会被浏览器悄悄改变,要用的话你应该提前把属性复制到自己的变量里,比如代码里的 const t = e.currentTarget 就是把 currentTarget 的引用复制到 t。
下面来看一下 target 和 currentTarget的区别:
e.target 是用户操作的元素
e.currentTarget 是程序员监听的元素
一个特例:
level7.addEventListener('click', ()=>{
console.log(1)
}) //冒泡
level7.addEventListener('click', ()=>{
console.log(2)
}) //捕获
// 打印结果为 1,2
level7.addEventListener('click', ()=>{
console.log(2)
},true) //捕获
level7.addEventListener('click', ()=>{
console.log(1)
}) //冒泡
//打印结果为 2,1
只有一个 div 被监听(不考虑父子同时被监听)
fn 分别在捕获阶段和冒泡阶段监听 click 事件
用户点击的元素就是开发者监听的
取消冒泡
捕获不可取消,但冒泡可以
e.stopPropagation() 可中断冒泡,浏览器不再向上走
一般用于封装某些独立的组件