事件流就是事件的传播机制,描述的是从页面中接受事件的顺序。

当你点击一个按钮时,单击事件不仅仅发生在按钮上,也单击了按钮的容器元素,甚至也单击了整个页面。

  1. <div class="outer">
  2. <div class="center">
  3. <div class="inner"></div>
  4. </div>
  5. </div>
  1. let outer = document.getElementsByClassName('outer')[0],
  2. center = document.getElementsByClassName('center')[0],
  3. inner = document.getElementsByClassName('inner')[0];
  4. outer.onclick = function() {
  5. console.log('outer')
  6. }
  7. center.onclick = function() {
  8. console.log('center')
  9. }
  10. inner.onclick = function() {
  11. console.log('inner')
  12. }

点击 OUTER 输出 outer,点击 CENTER 输出 outer、center,点击 INNER 输出 outer、center、inner

1. 事件冒泡

事件冒泡,即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接受,然后沿 DOM 树逐级向上传播到较为不具体的节点(文档)。IE 低版本浏览器只冒泡到 document 为止,IE9 及其它浏览器会冒泡到 window 对象。

IE 低版本的事件流,只支持事件冒泡。

2. 事件捕获

事件捕获的思想是不太具体的节点应该先接受到事件,然后沿 DOM 树依次向下,而最具体的节点应该是最后接受到事件。事件捕获的用意在于在事件到达预定目标之前捕获它。

尽管 DOM 2 级事件规范要求事件应该从 document 对象开始传播,但是大多浏览器都是从 window 对象开始捕获事件。

3. DOM 事件流

DOM 2 级事件规定的事件流包括三个阶段:

  • 事件捕获阶段

  • 处于目标阶段

  • 事件冒泡阶段

首先发生的是事件捕获,为截获事件提供了机会,然后实际目标接受到事件,最后是冒泡阶段,可以在这里对事件做出响应。

即使 DOM 2 级事件规范明确要求捕获阶段不会涉及事件目标,但是大多数浏览器都会在捕获阶段触发事件对象上的事件,结果就是有两个机会在目标对象上操作事件。

JS 事件流 - 图1

捕获阶段:
从最外层开始查找 -> document -> outer -> center -> inner 捕获到精确的哪个元素触发事件

冒泡阶段:
从最里层元素开始 -> inner -> center -> outer -> document -> window 一直冒泡到最外层

使用 on 的事件都是 DOM 0 级事件,事件绑定函数的执行,都是在冒泡阶段,只要绑定了事件函数,就会按照冒泡顺序执行,单击事件会比双击事件先触发执行。

DOM 2级事件,可以通过传入第三个参数,来将函数绑定在捕获阶段或者冒泡阶段执行。

4. 取消冒泡

使用事件对象的 stoppropagation() 方法,可以阻止冒泡,但是 IE 的事件对象不支持这种方法,IE 的事件对象中,可以使用将 cancelBubble 属性置为 true 来阻止。

inner.onclick = function(e) {
  e = e || window.event;
  e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true
}