lgwebdream / FE-Interview

🔥🔥🔥 前端面试,独有前端面试题详解,前端面试刷题必备,1000+前端面试真题,Html、Css、JavaScript、Vue、React、Node、TypeScript、Webpack、算法、网络与安全、浏览器
https://lgwebdream.github.io/FE-Interview/
Other
6.76k stars 897 forks source link

Day385:在一个 DOM 上同时绑定两个点击事件:一个用捕获,一个用冒泡。事件会执行几次?先执行冒泡还是捕获? #1220

Open Genzhen opened 2 years ago

Genzhen commented 2 years ago

每日一题会在下午四点在交流群集中讨论,五点小程序中更新答案 欢迎大家在下方发表自己的优质见解

二维码加载失败可点击 小程序二维码

扫描下方二维码,收藏关注,及时获取答案以及详细解析,同时可解锁800+道前端面试题。


参考分析

冒泡是从下向上,DOM 元素绑定的事件被触发时,此时该元素为目标元素,目标元素执行后,它的祖先元素绑定的事件会向上顺序执行。

addEventListener 函数的第三个参数设置为 false,说明不为捕获事件,即为冒泡事件。

捕获则和冒泡相反,目标元素被触发后,会动目标元素的最顶层祖先元素往下执行到目标元素为止。

当一个元素绑定了两个事件,一个是冒泡,一个是捕获。

首先需要明确的是,绑定了几个事件就会执行几次。

对于执行顺序的问题需要注意一下。该 DOM 上的事件如果被触发,会有这几种情况。

因为 W3C 标准有说明,先发生捕获事件,后发生冒泡事件。所有事件的顺序是:其它元素捕获阶段事件-> 本元素代码书序事件 ->其它元素冒泡阶段事件

另外需要注意的是,在冒泡阶段,向上执行的过程中,已经执行过的捕获事件不再执行,只执行冒泡事件。

longjinxiang commented 2 years ago

如果该 DOM 是目标元素,则按事件绑定顺序执行,不区分冒泡/捕获 <<< 这句话有问题,测试以下代码,都是先执行捕获,再执行冒泡,与事件绑定顺序无关

window.addEventListener('click', () => {console.log('aa')}) window.addEventListener('click', () => {console.log('bbbbbbb')}, true)

specialgirlgotoheaven commented 2 years ago

chrome,firefox都支持两种事件,捕获和冒泡,IE仅支持冒泡

Zenquan commented 2 years ago
 <div id="btn">点击
    <div id="btn2" style="margin: 100px;">
      点击2
    </div>
  </div>
  <script>
    const btn = document.getElementById('btn')
    const btn2 = document.getElementById('btn')
    btn.addEventListener('click', () => {console.log('aa')})
    btn.addEventListener('click', () => {console.log('bb')}, true)

    btn2.addEventListener('click', () => {console.log('cc')})
    btn2.addEventListener('click', () => {console.log('dd')}, true)
  </script>

在chrome的结果都是

"bb"
"dd"
"aa"
"cc"
QQhuan commented 2 years ago

老哥,经过实践,触发目标元素的点击事件,不论绑定捕获冒泡的顺序如何更改,回调的顺序都是先捕获再冒泡!!!火狐谷歌都是... 如果是我理解有误麻烦指正,如果是参考分析有误还请订正。

bigbigfan commented 2 years ago

公众号好像不维护了。但是还是帮忙留言chrome的实际行为是「该场景下捕获总是在冒泡之前触发」

whenTheMorningDark commented 2 years ago

如果目标DOM元素有事件,则先事件,后捕获,然后在冒泡。

YuArtian commented 2 years ago
 <div id="btn">点击
    <div id="btn2" style="margin: 100px;">
      点击2
    </div>
  </div>
  <script>
    const btn = document.getElementById('btn')
    const btn2 = document.getElementById('btn')
    btn.addEventListener('click', () => {console.log('aa')})
    btn.addEventListener('click', () => {console.log('bb')}, true)

    btn2.addEventListener('click', () => {console.log('cc')})
    btn2.addEventListener('click', () => {console.log('dd')}, true)
  </script>

在chrome的结果都是

"bb"
"dd"
"aa"
"cc"

哈哈哈哈哈哈哈哈哈

robert88 commented 2 years ago
const btn2 = document.getElementById('btn') 错误了
houchaowei commented 8 months ago

chrome,firefox都支持两种事件,捕获和冒泡,IE仅支持冒泡

我觉得这个得加上浏览器的兼容性问题,脱离兼容性说这个,有点缺乏完整性,很多回答是ie之外的浏览器,确实是不论绑定的事件的先后顺序,先捕获后冒泡,这个可以结合react中实现的合成事件来说或许更完整些,react的合成事件就是基于兼容性,为了磨平浏览器的差异,实现了一套类似冒泡阶段执行事件的机制,在合成事件中,只在冒泡阶段执行一次。