事件冒泡
一个问题
如果有下面一段代码:
- 在 HTML 中,定义了嵌套的 3 个
<div/>
,ID 分别为 div1、div2、div3。 - 在 JavaScript 中定义一个点击事件处理函数,打印当前执行事件元素的 id。
- 把事件处理函数分别注册到这 3 个 div 中。
这个时候,如果点击最里边的 div3 元素,你猜代码的输出结果是什么?
// html: <div id="div1"> div1 <div id="div2"> div2 <div id="div3">div3</div> </div> </div>; // javascript: function handleClick(event) { console.log(event.currentTarget.id); } for (let i = 1; i <= 3; i++) { let div = document.getElementById(`div${i}`); div.addEventListener("click", handleClick); }
选项 | |
---|---|
A. | div3 |
B. | div3, div2, div1 |
答案是:B。 为什么结果会是这样呢?这得从 JavaScript 的事件冒泡机制说起。
什么是事件冒泡机制
事件冒泡机制是说,在给某个 DOM 元素设置了事件监听器之后,例如鼠标点击事件,在触发事件时,浏览器不仅仅执行该元素的事件处理函数,还会顺着 DOM 树的结构,一级一级的往上查找,是不是有父级元素注册了相同的事件监听器,如果有的话,还会执行父级元素的事件处理函数,直到 <html />
根元素为止。
在上边的例子中,点击了 div3 元素之后:
- 先执行了 div3 的点击事件处理函数,此时 event.currentTarget 是 div3 这个元素,打印出它的 id 为 div3。
- 接着浏览器会查找上级元素 div2 有没有点击事件监听,结果有,那么就执行了它的处理函数打印出 div2。
- 同理,打印出最外层的 div1 之后,就再没有点击事件监听了,这个时候事件才停止冒泡了。
阻止事件冒泡机制
事件冒泡机制会给开发中带来意想不到的结果,要阻止它也很简单,只需要在事件处理函数中,调用事件参数的 stopPropagation() 方法,这样事件在当前元素处理完成之后就结束了,不会再查找父级组件相同的事件监听了。
event.stopPropagation();
如果我们在之前的例子中,事件处理函数里,加上这段代码,那么,再点击最里边的 div3,就只会打印出 div3 了:
function handleClick(event) {
event.stopPropagation();
console.log(event.currentTarget.id);
}
对于一些老的浏览器,例如 IE9 以下,如果要阻止事件冒泡,需要设置 event 参数的 cancelBubble 属性为 true:
event.cancelBubble = true;
这样也就阻止事件冒泡了。如果以后遇到了同时触发了多个事件的情况,可以先想一下是不是事件冒泡机制捣的鬼,如果是就把它停止。