当浏览器发展到了第四代的时候,也就是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

可以看出先进行的事件捕获,一直到目标阶段,然后再冒泡上去,用图示例就是:

img

上面还有一个关键点需要注意:如果我们在目标元素上同时监听了事件冒泡和事件捕获,他会按照绑定的顺序来执行

参考

  • JavaScript高级程序设计第三版, 事件流