今天我们来聊一下一个简单的面试题!描述一下js中的事件触发过程!
js中的事件捕获和事件冒泡
在JavaScript中,事件传播机制包括两种主要方式:事件捕获(Capturing)和事件冒泡(Bubbling)。这两种方式决定了当在DOM(文档对象模型)中的某个元素上触发一个事件时,该事件如何在该元素及其祖先元素之间传播。
事件捕获(Event Capturing)
事件捕获阶段从DOM树的最顶层开始,即window
对象,然后逐级向下,直到触发事件的目标元素。在这个过程中,事件会从最不具体的节点(通常是document
或window
)开始,沿着DOM树向下传播到最具体的节点(即事件的目标元素)。如果在捕获阶段有事件监听器被触发,并且它们中的任何一个调用了event.stopPropagation()
方法,那么事件传播会立即停止,不会到达目标元素。
在事件监听器中,你可以通过将第三个参数设置为true
来指定在捕获阶段处理事件:
javascript
element.addEventListener('click', function(event) {
// 处理事件
}, true); // 第三个参数为true,表示在捕获阶段处理事件
事件冒泡(Event Bubbling)
事件冒泡阶段则正好相反,它从触发事件的目标元素开始,然后逐级向上传播,直到最顶层的window
对象。在冒泡阶段,事件会从最具体的节点开始,然后沿着DOM树向上传播到最不具体的节点。如果在冒泡阶段有事件监听器被触发,并且它们中的任何一个调用了event.stopPropagation()
方法,那么事件传播会立即停止,不会继续向上传播。
默认情况下,如果你没有指定事件监听器应该在哪个阶段处理事件,那么它会在冒泡阶段处理事件。你也可以通过将第三个参数设置为false
来明确指定在冒泡阶段处理事件:
javascript
element.addEventListener('click', function(event) {
// 处理事件
}, false); // 第三个参数为false或省略,表示在冒泡阶段处理事件
事件传播顺序
事件传播的完整顺序是:捕获阶段 -> 目标元素 -> 冒泡阶段。也就是说,如果一个元素及其祖先元素都注册了相同类型的事件监听器,那么这些监听器将按照上述顺序被触发。
阻止事件传播
event.stopPropagation()
方法用于阻止事件在DOM树中进一步传播。无论是在捕获阶段还是冒泡阶段,调用这个方法都会阻止事件继续传播。
event.stopImmediatePropagation()
方法的行为与event.stopPropagation()
类似,但它还会阻止同一元素上的多个相同事件被触发。
js事件触发的过程
我们来通过一个小Demo来具体阐述一下js事件的触发过程
html
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app{
width: 400px;
height: 400px;
background-color: aqua;
}
#wrap{
width: 200px;
height: 200px;background-color: rebeccapurple;
}
#box{
width: 100px;
height: 100px;
background-color: darkorange;
}
</style>
</head>
<body>
<div id="app">
<div id="wrap">
<div id="box">
</div>
</div>
</div>
<script>
let app = document.getElementById("app");
let wrap = document.getElementById("wrap");
let box = document.getElementById("box");
// addEventListener有第三个参数,默认是false,true表示在捕获阶段触发,false表示在冒泡阶段触发
app.addEventListener('click',()=>{//绑定,订阅,注册
console.log("app")
})
wrap.addEventListener('click',()=>{
console.log("wrap")
})
box.addEventListener('click',()=>{
console.log("box")
})
</script>
</body>
我们写了三个嵌套容器app
->wrap
->box
我们可以用一个图来描述js的事件触发过程!
事件触发过程分为三个阶段:事件捕获、目标元素阶段和事件冒泡。
- 事件捕获 :这是事件传播的第一个阶段,它从DOM树的最顶层(通常是
window
对象或document
对象)开始,沿着DOM树向下传播,直到触发事件的具体元素。在这个过程中,如果某个节点注册了捕获阶段的事件监听器,那么该监听器会在事件到达目标元素之前被触发。 - 目标元素阶段:当事件到达触发它的具体元素时,该元素上注册的事件监听器会被触发。这是事件传播的第二个阶段。
- 事件冒泡 :这是事件传播的第三个阶段,它从目标元素开始,然后沿着DOM树向上传播,直到最顶层的
window
对象。在这个过程中,如果某个节点的祖先元素注册了冒泡阶段的事件监听器,那么这些监听器会被依次触发。
大致过程:
在window上往事件触发处传播,遇到注册的 捕获 事件会触发(我们可以让他触发)
到达了事件触发处
从事件触发处往window上传播,遇到的注册的 冒泡 事件会触发
从事件触发处,往window上传播,这个过程叫冒泡过程,这个过程中触发的所有事件就叫冒泡事件
从外层容器,一步一步向内层容器传播,这个过程叫捕获过程,这个过程触发的所有事件叫捕获事件
我们回到我们的Demo上来,现在我们的页面上有三个嵌套的容器,每个容器都添加了点击事件

我们来点击一下box
看看输出结果:
js
输出:
box
wrap
app
为什么是这样的结果呢?这个因为addEventListener
有第三个参数,默认为false
,所以事件的触发就默认在冒泡过程当中!也就是类似回溯的过程当中,从box
容器,触发事件,传播到wrap
容器,触发事件,再传播到app
容器,触发事件。
如果我们把第三个参数修改为ture
,我们再来看看输出结果
js
app.addEventListener('click',()=>{//绑定,订阅,注册
console.log("app")
},true)
wrap.addEventListener('click',()=>{
console.log("wrap")
},true)
box.addEventListener('click',()=>{
console.log("box")
},true)
我们再点击一下box
容器,此时的输出结果就会变为
js
输出:
app
wrap
box
当我们将第三个参数修改为ture
,事件就会在捕获过程中被触发!也就是从最外层容器开始传播,从app
容器,事件触发,传播到wrap
容器,事件触发,传播到box
容器,事件触发。
阻止默认事件传播的方法
当我们在box
容器的事件监听器中加入这个方法event.stopPropagation();
js
app.addEventListener('click',()=>{//绑定,订阅,注册
console.log("app")
},false)
wrap.addEventListener('click',()=>{
console.log("wrap")
},false)
box.addEventListener('click',()=>{
console.log("box")
event.stopPropagation();
},false)
此时,我们再点击box
容器,此时的输出结果只有box
,说明这个方法会阻止冒泡过程的传播!
同样的,这个方法也能阻止捕获过程的传播!
js
app.addEventListener('click',()=>{//绑定,订阅,注册
console.log("app")
event.stopPropagation()
},true)
wrap.addEventListener('click',()=>{
console.log("wrap")
},true)
box.addEventListener('click',()=>{
console.log("box")
},true)
此时,我们在app
容器中,加入这个方法,同时将事件监听器的第三个参数改为ture
,在捕获过程中触发事件,我们再点击box
容器看看输出结果!
此时输出只有app
也就是说event.stopPropagation();
会阻止默认事件的传播。
同时,还有event.stopImmediatePropagation()
这个方法不仅仅包括stopPropagation()
的功能,同时还可以阻止同一dom绑定多个相同事件
例如:
js
app.addEventListener('click',()=>{//绑定,订阅,注册
console.log("app")
})
wrap.addEventListener('click',()=>{
console.log("wrap")
})
box.addEventListener('click',()=>{
console.log("box")
event.stopImmediatePropagation()
})
box.addEventListener('click',()=>{
console.log("box2")
})
box.addEventListener('mouseenter',()=>{
console.log("box3")
})
我们还是在冒泡过程处理事件,我们给box
添加了两个点击事件和一个鼠标移入事件,一个输出box
,一个输出box2
,鼠标移入事件输出box3
我们再点击box
容器,来看看输出结果!

我们可以看到event.stopImmediatePropagation()
不仅仅阻止了默认事件的传播,还会阻止同一dom结果绑定的多个相同的事件!
最后
今天我们主要学习了js中的事件触发过程,以及阻止事件传播的两个方法!
这个知识点对于我们在js中处理Dom事件,相当重要!可以帮助我们灵活地控制事件的处理方式,特别是在复杂的Dom结构和多个事件监听器的情况下。
如果,你觉得这篇文章有帮助的话,可以帮博主点赞+评论+收藏,三连一波!感谢!
往后,我还会持续输出相关的文章,如node相关的内置模块,koa的使用等等,感兴趣的小伙伴可以关注一波!