JavaScript中的事件处理机制分为两个阶段:捕获和冒泡。当用户触发一个事件,它会沿着DOM树的传递路径向下传递,直到达到最终目标,然后再反向传播回来。捕获阶段发生在冒泡阶段之前,所以先触发捕获事件,再触发冒泡事件。下面来详细介绍一下JavaScript冒泡和捕获。
下面我们以一个点击事件的例子来介绍。
let grandfather = document.getElementById("grandfather");
let father = document.getElementById("father");
let son = document.getElementById("son");
// 冒泡
function bubbleHandler() {
console.log("bubble: " + this.id);
}
// 捕获
function captureHandler() {
console.log("capture: " + this.id);
}
grandfather.addEventListener("click", bubbleHandler);
father.addEventListener("click", bubbleHandler);
son.addEventListener("click", bubbleHandler);
grandfather.addEventListener("click", captureHandler, true);
father.addEventListener("click", captureHandler, true);
son.addEventListener("click", captureHandler, true);
如上所述,我们在HTML中嵌套了三个div。然后使用addEventListener函数来添加了三个点击事件处理程序,并在这三个事件处理程序中添加了两个事件。第一次是冒泡事件,第二次是捕获事件。addEventListener函数里的第三个参数(默认为false)表示点击事件用的是冒泡还是捕获。如果为true,表示用捕获,否则表示用冒泡。
现在我们分别点击grandfather,father和son这几个元素,看看它们触发的顺序。我们可以看到在父元素中添加了冒泡事件处理程序后,当我们点击子元素时会触发该事件处理程序,然后将该事件逐级传递到祖先元素上。当祖先元素捕获该事件时,它就会调用捕获事件处理程序。下面是控制台输出结果:
// 从son元素开始,冒泡到grandfather:
bubble: son
bubble: father
bubble: grandfather
// 从grandfather元素开始,捕获到son:
capture: grandfather
capture: father
capture: son
我们可以看到,在冒泡阶段,点击son时,先打印"bubble: son",随后是"bubble: father",最后是"bubble: grandfather",即事件自下而上冒泡到最外层元素。而在捕获阶段,先打印"capture: grandfather",随后是"capture: father",最后是"capture: son",即事件从最外层元素开始,自上而下捕获到最内层元素。
总之,JavaScript提供了两种事件处理机制,分别是捕获和冒泡。当事件发生时,我们可以选择将事件先在最外层元素上捕获,再逐级向下传递,直到传递到目标元素。或者选择先在目标元素上触发事件,然后由目标元素向上传递到最外层元素。在实际开发中,我们应该根据具体的业务需求来选择相应的事件机制。