事件捕获冒泡以及代码示例详解

文档收录至 web前端知识空间www.nqone.com/…

当点击一个按钮,触发点击事件,页面中事件触发顺序是怎样的呢?

触发流程

一个元素触发事件时, 事件触发流程是这样的, 先从顶部 document 开始向下捕获直到触发事件的目标元素, 然后目标元素事件触发, 接着开始向上冒泡, 直到根元素 document。使用 事件触发后 eventPhase 属性就能查看到当前事件绑定的方法是在哪个阶段触发了。

eventPhase 取值为数字 0、1、2、3,如果想要打印查看结果的时候,查看最准确的值需要直接打印该属性的值。如下是分别对应含义:

  • 0:没有事件发生;
  • 1:事件捕获阶段触发;
  • 2:事件触发目标元素本身触发;
  • 3:事件冒泡阶段触发;

如果使用 addEventListener 绑定的该类事件, 第三个方法为 true 时, 在向下捕获的过程中, 对应的事件绑定的方法便会触发。如果为 false, 那么绑定的该类事件只会在 从目标元素向上冒泡时才会触发。

简而言之:事件在页面触发流程是这样的, 顶级元素开始捕获 => 事件触发目标元素 => 向上冒泡至顶级元素

addEventListener

用来给元素绑定一个事件,有三个参数,第一个参数事件名称,第二个参数为绑定事件触发需要执行的方法,第三个 bool 参数用来定义捕获阶段触发(true)还是冒泡阶段触发(false),默认为 false,一般使用时最好写明,以防不同浏览器版本等有差异。

第三个参数还有可能是对象, 如果是对象, 含义如下:

  • capture: 表示 listener 会在该类型的事件捕获阶段传播到该 EventTarget 时触发
  • once: 表示 listener 在添加之后最多只调用一次。如果为 true,listener 会在其被调用之后自动移除
  • passive: 设置为 true 时,表示 listener 永远不会调用 preventDefault()。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。查看使用 passive 改善滚屏性能以了解更多
  • signal: 该 AbortSignal 的 abort() 方法被调用时,监听器会被移除

stopPropagation

    该方法用来**阻止**同类**事件的进一步传播**,如果是**捕获过程**,将会**终止事件的向下捕获**,如果是冒**泡过程**,将会终**止事件的向上冒泡**。

    需要特别注意:因为事件传递流程是**先捕获后冒泡**,所以如果在捕获阶段调用了该方法,那么从顶级向下捕获时,会在对应的位置停止事件传播,之后的 捕获阶段 和 目标事件 以及 冒泡 等均不会再执行,使用时需要注意。

     比如 在 document 上 使用 addEventListener 绑定了点击事件,然后调用了 stopPropagation 方法,子元素绑定点击事件时,那么基本上只会触发 document 上的点击事件,其他均不会触发。

详情示例

HTML 代码如下所示,定义了层层元素以及添加类名:

<body>
  <div class="out-div">
    <button class="out-btn">out</button>
    <div class="middle-div">
      <button class="middle-btn">middle</button>
      <div class="inner-div">
        <button class="inner-btn">inner</button>
      </div>
    </div>
  </div>
</body>
<script src="./addEventListener.js"></script>

addEventListener.js 代码如下所示:

// 注意,千万不要直接打印 evt 来查看,因为对象引用只会打印最后的值,要么debugger查看,要么直接打印对应的固定值
document.addEventListener('click', (evt) => {
  /** 如果在此处调用,那么页面上只会存在如下的打印 */
  // evt.stopPropagation();
  console.log('document', evt.eventPhase);
}, true);

const outDiv = document.querySelector('.out-div');
outDiv.addEventListener('click', (evt) => {
  console.log('out-div =>', evt.eventPhase);
}, false);

const outBtn = document.querySelector('.out-btn');
outBtn.addEventListener('click', (evt) => {
  console.log('out-btn =>', evt.eventPhase);
}, false);

const middleDiv = document.querySelector('.middle-div');
middleDiv.addEventListener('click', (evt) => {
  // evt.stopPropagation();
  console.log('middle-div =>', evt.eventPhase);
}, true);

const middleBtn = document.querySelector('.middle-btn');
middleBtn.addEventListener('click', (evt) => {
  console.log('middle-btn =>', evt.eventPhase);
}, false);

const innerDiv = document.querySelector('.inner-div');
innerDiv.addEventListener('click', (evt) => {
  // evt.stopPropagation();
  console.log('inner-div =>', evt.eventPhase);
}, false);

const innerBtn = document.querySelector('.inner-btn');
innerBtn.addEventListener('click', (evt) => {
  console.log('inner-btn =>', evt.eventPhase);
}, false);

当在页面中点击最内部一个按钮,也就是 类名为 “inner-btn” 的按钮,打印结果如下所示:
在这里插入图片描述
从图中可以看出,事件执行是先往目标元素捕获,触发对应的使用捕获的方式监听的事件,到达触发源,在往上冒泡,触发使用冒泡方式绑定的事件。

   如果该 demo 在 document 绑定的点击事件中调用 evt.stopPropagation();,及代码中的注释部分打开,那么页面中将只会打印 document 1,其他的均不会触发。
  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值