捕获:当用户点击按钮,浏览器会从 window 从上向下遍历至用户点击的按钮,逐个触发事件处理函数。 冒泡:浏览器从用户点击的按钮从下往上遍历至 window,逐个触发事件处理函数。

W3C 事件模型/事件机制:对每个事件先捕获再冒泡

先从点击事件开始说起

  1. <div class= 爷爷>
  2. <div class= 爸爸>
  3. <div class= 儿子>
  4. 文字
  5. </div>
  6. </div>
  7. </div>

当给三个div分别添加事件监听 fnYe/fnBa/fnEr

问题:点击文字,最先调用那个函数?
IE5 认为先调用fnEr
网景认为先调用fnYe

W3C 发布标准:
规定浏览器应该同时支持两种调用顺序

首先按 爷爷=>爸爸=>儿子 顺序看有没有函数监听
然后按 儿子=>爸爸=>爷爷 顺序看有没有函数监听
有监听函数就调用,并提供事件信息,没有就跳过

像这种
从外向内找监听函数,叫事件捕获
从内向外找监听函数,叫事件冒泡
image.png

事件绑定API

image.png
如果 bool 不传或为 falsy
就让 fn 走冒泡,即当浏览器在冒泡阶段发现 baba 有 fn 监听函数,就会调用 fn,并提供事件信息


一个点击事件样例
image.png

在事件结束后 e 会自动消亡,鼠标点击动作结束后 e 就消亡了
获取点击的状态,可以使用 currentTarget

  1. level1.addEventListener('click', (e)=>{
  2. const t = e.currentTarget
  3. setTimeout(()=>{
  4. t.classList.remove('x')
  5. },n*1000)

在事件结束后,不推荐大家继续用 e,因为 e 会被浏览器悄悄改变,要用的话你应该提前把属性复制到自己的变量里,比如代码里的 const t = e.currentTarget 就是把 currentTarget 的引用复制到 t。

下面来看一下 target 和 currentTarget的区别:
e.target 是用户操作的元素
e.currentTarget 是程序员监听的元素


一个特例:

  1. level7.addEventListener('click', ()=>{
  2. console.log(1)
  3. }) //冒泡
  4. level7.addEventListener('click', ()=>{
  5. console.log(2)
  6. }) //捕获
  7. // 打印结果为 1,2
  8. level7.addEventListener('click', ()=>{
  9. console.log(2)
  10. },true) //捕获
  11. level7.addEventListener('click', ()=>{
  12. console.log(1)
  13. }) //冒泡
  14. //打印结果为 2,1

只有一个 div 被监听(不考虑父子同时被监听)
fn 分别在捕获阶段和冒泡阶段监听 click 事件
用户点击的元素就是开发者监听的

取消冒泡

捕获不可取消,但冒泡可以
e.stopPropagation() 可中断冒泡,浏览器不再向上走
一般用于封装某些独立的组件