谈谈JS事件冒泡和事件捕获
前言
JavaScript 与 HTML 的交互是通过事件实现的。
而事件流描述了页面接收事件的顺序。DOM2 Event 规范规定事件流分为3个阶段:事件捕获、到达目标和事件冒泡,如下图。非常有意思的是 IE 和 Netscape(网景) 开发团队提出了几乎完全相反的事件流方案。IE 支持事件冒泡流,而 Netscape 将支持事件捕获流。后来 w3c 采用折中的方式,制定了统一的标准——先捕获再冒泡。
一、事件冒泡
事件冒泡可以形象地比喻为把一颗石头投入水中,泡泡会一直从水底冒出水面。也就是说,事件会从最内层的元素开始发生,一直向上传播,直到到根结点。
接下来我们通过一个简单的案例来说明事件冒泡:
<body> <div id="div1"> div1 <div id="div2"> div2 <div id="div3">这是div3</div> </div> </div> <script> let div1 = document.getElementById('div1'); let div2 = document.getElementById('div2'); let div3 = document.getElementById('div3'); div1.addEventListener('click',function(){ console.log("这是div1的点击事件"); },false); div2.addEventListener('click',function(){ console.log("这是div2的点击事件"); },false); div3.addEventListener('click',function(){ console.log("这是div3的点击事件"); },false); </script> </body>
addEventListener(event, function, useCapture)方法用于向指定元素添加事件 第一个参数是需要绑定的事件 第二个参数是触发事件后要执行的函数 第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数。(可不选)
当我们只点击 div3 时,控制台出现如下结果:
这里我们要注意,我们传递的仅仅是事件触发,也就是说当点击div3仅仅触发了父级的点击事件,并没有把自己的绑定的函数给父级,父级的执行情况,取决于自己所绑定的函数。在有些时候事件冒泡也会给我们带来困扰,例如在我们点击div3时,我们不希望div1和div2触发事件。这有什么办法解决吗?
- 方法:在相应的处理函数内,加入 event.stopPropagation(),终止气泡往上冒,这样事件停留在本节点,不会再往外传播了。
<script> let div1 = document.getElementById('div1'); let div2 = document.getElementById('div2'); let div3 = document.getElementById('div3'); div1.addEventListener('click',function(){ console.log("这是div1的点击事件"); event.stopPropagation(); },false); div2.addEventListener('click',function(){ console.log("这是div2的点击事件"); event.stopPropagation(); },false); div3.addEventListener('click',function(){ console.log("这是div3的点击事件"); event.stopPropagation(); },false); </script>
![](https://img2020.cnblogs.com/blog/2580638/202110/2580638-20211026203333544-1396201279.png)
点击div3后不会出现冒泡
其实事件冒泡不仅会给我们带来困扰,还会给我们带来优点,那就是事件委托,关于事件委托的内容会在下一篇详细介绍。
二、事件捕获
与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。
将上述事件冒泡代码中的false全都改为true变成变成事件捕获阶段调用函数,再次点击div3会出现如下情况:
可以看出和冒泡事件正好相反,那么它两谁的优先级高呢?让我们把div1改为true变成事件捕获再次点击div3来试一试:
可以看出div1先执行了,由此可以得出:事件捕获优先于事件冒泡。
补充
并不是所有的事件都会产生冒泡,例如以下事件就不会产生冒泡:
焦点事件
- blur(当一个元素失去焦点的时候被触发。它和
focusout
事件的主要区别是 focusout 支持冒泡。) - focus (当一个元素获取焦点的时候被触发)
UI事件
- load
- unload
- scroll (滚动窗口被触发)
鼠标事件
- mouseenter
- mouseleave