在js中存在事件冒泡与事件捕获两种概念,这两个概念都是为了解决页面中事件流(事件发生顺序)的问题。
事件冒泡(dubbed bubbling)
事件冒泡我们从字面意思理解就是当用户行为触发我们页面的定义好的事件后,会有一个由内到外的一个冒泡过程,而不是一下子就命中事件绑定的元素
事件捕获(event capturing)
事件捕获与冒泡恰恰相反,当鼠标点击或者触发dom事件时,浏览器会从根节点开始由外到内进行事件传播,即点击了子元素,如果父元素通过事件捕获方式注册了对应的事件的话,会先触发父元素绑定的事件
我们用代码来理解一下
在下边这个例子中,如果两个元素都有一个click的处理函数,那么我们怎么才能知道哪一个函数会首先被触发呢?为了解决这种脑壳痛的问题,就有了今天的主题冒泡与捕获
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>JS事件处理程序</title> <link href="https://cdn.bootcss.com/skeleton/2.0.4/skeleton.min.css" rel="stylesheet"> </head> <body> <div class="wrapper" id="wrapper"> <div class="container"> <button id="event">JS事件处理程序</button> </div> </div> <script> var wrapper = document.getElementById("wrapper"), event = document.getElementById("event"); wrapper.addEventListener("click", function () { alert("1"); console.log("1"); }, true) event.addEventListener("click", function () { alert("2"); console.log("2"); }, true) wrapper.addEventListener("click", function () { alert("3"); console.log("3"); }, false) event.addEventListener("click", function () { alert("4"); console.log("4"); }, false) </script> </body> </html>
上面的代码最终输出结果为:
-
1
-
2
-
4
-
3
在DOM2级事件规定的时间流包括 三个阶段:
-
事件捕获阶段
-
处于目标阶段
-
事件冒泡阶段
我们可以看到addEventListener的第三个参数我们传入的不同,也就是上面结果为什么没有顺序执行的原因,这里我们在来回顾一下addEventListener
addEventListener
语法: target.addEventListener(type, listener[, useCapture])
useCapture 可选Boolean, 当useCapture为true 事件句柄在捕获阶段执行,也就是事件由外向内执行 当useCapture为false 事件句柄在冒泡阶段执行,也就是事件由内向外执行
看到这也应该了解上面代码为何会输出1243了
好了,回来业务中,实际应用场景我们肯定不愿同时触发多个click, 在点击子元素时我们不想触发父元素的事, 这个时候我们就可以使用stopPropagation来停止事件冒泡
代码理解
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>JS事件处理程序</title> <link href="https://cdn.bootcss.com/skeleton/2.0.4/skeleton.min.css" rel="stylesheet"> </head> <body> <div class="wrapper" id="wrapper"> <div class="container"> <button id="event">JS事件处理程序</button> </div> </div> <script> var wrapper = document.getElementById("wrapper"), event = document.getElementById("event"); wrapper.onclick = function () { console.log('捕获阶段执行父元素wrapper的事件处理程序') } event.onclick = function () { // 阻止事件冒泡 e.stopPropagation() console.log('冒泡阶段执行子元素event的事件处理程序') } </script> </body> </html>
上面的示例中我们使用了stopPropagation来阻止冒泡,所以此时只会触发一个事件了
总结
-
事件冒泡由内向外执行
-
事件传播由外向内执行
-
addEventListener的第三个参数可以改变事件流的状态
-
使用stopPropagation可以阻止冒泡