当浏览器发展到了第四代的时候,也就是IE4和Netscape Communicatot4,浏览器开发团队遇到了一个很有意思的问题,页面的那一部分会拥有某个特定的事件?要明白这个问题是什么,你可以想象画在纸上的一组同心圆,如果你把手指放在圆心上,那么你的手指指向的不是一个圆,而是所有的圆。两家公司的浏览器开发团队在看待浏览器对待事件方面还是一致的。如果你单击了某个按钮,他们都认为单击事件不仅仅发生在按钮上,换句话说,在单击按钮的同时,你也单击了按钮的容器元素,甚至也单击了整个页面。
事件流描述的是从页面接收事件的顺序,但有意思的是,IE和Netscape开发团队居然提出了差不多完全相反的事件流的概念。IE的事件是事件冒泡流,而Netscape Communicator的事件流是事件捕获流。
事件冒泡
事件冒泡指的是事件依次从最内层的元素一直层层传递上面
div > body > html > document > window
事件捕获
事件捕获指的是事件依次从最外面的元素一直层层传递到目标元素
window > document > html > body > div
尽管“DOM2级事件”规范要求事件应该从document对象开始传播,但这些浏览器都是从window对象开始捕获事件的。
DOM2事件流
“DOM2级事件”规定的事件流包括三个阶段:
事件捕获 > 处于目标阶段 > 事件冒泡
下面是一段代码,我们从中感受这个流程。
<div id="box">
<span id="target">
Something
</span>
</div>
const box = document.getElementById('box');
const target = document.getElementById('target');
box.addEventListener('click', () => {
console.log('box bubbling');
}, false);
box.addEventListener('click', () => {
console.log('box capture');
}, true);
target.addEventListener('click', () => {
console.log('target capture');
}, true);
target.addEventListener('click', () => {
console.log('target bubbling');
}, false);
当我们点击按钮,会依次输出
// box capture
// target capture
// target bubbling
// box bubbling
可以看出先进行的事件捕获,一直到目标阶段,然后再冒泡上去,用图示例就是:
上面还有一个关键点需要注意:如果我们在目标元素上同时监听了事件冒泡和事件捕获,他会按照绑定的顺序来执行
参考
- JavaScript高级程序设计第三版, 事件流