冒泡和捕获

  • 在 Chrome 89.0.4363.0 以及之后版本中,目标元素(at_target)的触发事件顺序不再按照注册顺序触发!而是按照先捕获再冒泡的形式依次执行!

  • addEventListener 的第三个参数默认是 false,在冒泡阶段(bubble)触发

  • 阻止冒泡,e.stopPropagation

  • e.preventDefault() 可以阻止浏览器默认行为.比如,让a标签取消默认的跳转功能.

  • 为了避免因为版本的不同而造成业务上的问题,我们只需要将所有目标元素代码的顺序都按照先书写捕获事件代码,再书写冒泡事件代码,就可以兼容本次的更新。

# 如何识别事件是在捕获,目标对象,还有冒泡阶段进行的?

eventPhase 可以来识别

eventPhase等于0,这个时间,没有事件正在被处理

eventPhase等于1,捕获阶段

eventPhase等于2,事件处理程序处于目标对象上

eventPhase等于3,冒泡阶段调用的事件处理程序

注意:处于目标阶段时,捕获和冒泡都是 2

# 事件注册

一般使用 addEventListener 来注册事件,它接受三个参数:

  • 字符串:处理的事件名称,如点击事件 click;
  • 回调函数:事件处理程序,即要绑定的函数体;
  • 指定是在事件冒泡还是事件捕获阶段处理参数,可以是布尔值,也可以是对象
  • true 则作为捕获事件处理;
  • false 则作为冒泡事件处理(默认)。

第三个参数是对象时,可以使用以下几个属性:

  • capture:布尔值,和第三个参数作为布尔值时作用一样:false 为冒泡,true 为捕获
  • once:布尔值,值为 true 表示该回调只会调用一次,调用后会移除监听
  • passive:布尔值,表示永远不会调用 preventDefault

一般来说,我们可以通过使用 stopPropagation 来阻止事件的进一步传播,即阻止事件冒泡。

// stopPropagation() 可以阻止捕获和冒泡
e.stopPropagation();
// stopImmediatePropagation() 也可以阻止捕获和冒泡,但主要作用是阻止监听同一事件的其他事件监听器被调用
e.stopImmediatePropagation();
1
2
3
4
  • event.target 指向触发事件流程的元素,且不会改变。
  • this 指向的是当前所执行事件的注册元素,也就是 e.currentTarget。
  • 捕获止于 event.target,冒泡始于 event.target。
  • 主流浏览器都默认在冒泡阶段进行事件注册,所以,只有阻止冒泡的方法而没有阻止捕获的方法。

# 事件触发三阶段

  • 事件捕获阶段:window 往事件触发处传播,遇到注册的捕获事件会触发
  • 处于目标阶段:传播到事件触发处时触发注册的事件
  • 事件冒泡阶段:从事件触发处往 window 传播,遇到注册的冒泡事件会触发

假设在 DOM 结构里面有 text 的这样一个标签,给这个标签绑定了一个点击事件,那么在点击这个标签的时候是怎么执行事件的呢?

  • 首先是事件捕获阶段,会通过 window、document、body、div、text 这样的顺序一直往下捕获事件。
  • 然后是处于目标阶段,到 text 标签处触发绑定的点击事件。
  • 最后是事件冒泡阶段,事件是在冒泡阶段做出响应的。冒泡阶段通过 text 、div、body、document、window 这样的顺序往上冒泡,假如在 div 或者 body 上面也绑定了对应的 onclick 事件,那么会按顺序触发响应。

不同浏览器中事件的触发顺序不同,有些浏览器是按照代码注册的顺序,有些是先捕获再冒泡,为了不出 bug,建议在书写代码的时候按照先捕获再冒泡的事件顺序来书写。

# 事件代理

如果一个节点中的子节点是动态生成的,那么子节点需要注册事件的话可以注册在父节点上。

<ul id="proxy">
  <li>主页</li>
  <li>文章</li>
  <li>公告</li>
  <li>简介</li>
</ul>

let proxy = document.querySelector('#proxy')
proxy.addEventListener('click', (event) => {
  let target = event.target; // 当前点击的元素
  if (target.nodeName.toLowerCase() == 'li') {
    console.log('click:' + target.innerHTML);
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14

这种方式相较于直接给目标注册事件来说,有以下优点:

  • 可以减少内存占用,减少事件注册
  • 不需要给子节点注销事件
最近更新时间: 2021/08/25 17:25:09
最近更新
01
2023/07/03 00:00:00
02
2023/04/22 00:00:00
03
2023/02/16 00:00:00