事件流 —— 事件捕获和事件冒泡

2023/7/17 event

# 什么是事件流

JSHTML之间的交互是通过事件来完成的,我想让页面动起来,就要通过给 DOM 绑定事件来实现,而事件就是文档或浏览器窗口发生的一些特定的交互瞬间

事件流表示的就是事件接收和传播的过程,说白点就是一个事件发生的顺序

事件机制描述的是事件在 DOM 里面的传递顺序,以及我们可以对这些事件做出如何的响应

三个阶段:

  • 事件捕获阶段:事件从根节点从外向内往目标节点传播的阶段
  • 目标阶段:真正的目标节点正在处理事件的阶段
  • 事件冒泡阶段:事件从目标节点从内向外到根节点传播的阶段

这篇文章就来分析一下事件捕获和事件冒泡的原理

# 事件捕获

event capturing —— 重点就是从外向内传播事件

我们直接看下面这个例子👇

1

部分代码如下

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="box1">
      box1
      <div id="box2">
        box2
        <button id="child">点我测试</button>
      </div>
    </div>
  </body>
</html>
<script>
  window.addEventListener(
    "click",
    () => {
      console.log("Window触发了");
    },
    true
  );

  document.addEventListener(
    "click",
    () => {
      console.log("Document触发了");
    },
    true
  );

  document.querySelector("#box2").addEventListener(
    "click",
    () => {
      console.log("box2触发了");
    },
    true
  );

  document.querySelector("#box1").addEventListener(
    "click",
    () => {
      console.log("box1触发了");
    },
    true
  );

  document.querySelector("#child").addEventListener(
    "click",
    () => {
      console.log("点击了button");
    },
    true
  );
</script>

点击button,输出上面的结果

在这里我们使用了addEventListener方法来操作,需要注意的是第三个参数默认是false,表示在事件冒泡阶段调用事件处理函数,这里我们想让他们按照事件捕获阶段来输出,需要手动设置为true

通过结果我们可以看到,事件处理顺序是 Window -> Document -> box1 -> box2 -> child

而且需要注意的是,如果child还有子元素,那我们点击child,触发事件捕获的过程是不会传播到子元素上的

# 事件冒泡

同样的例子,我们修改addEventListener第三个参数为false,或者直接不写,默认就是false,我们来看一下事件冒泡的执行顺序结果

2

可以看到输出的顺序正好和事件捕获相反了 👇

child -> box2 -> box1 -> Document -> Window

# 阻止事件冒泡

看到这里,其实我们就会想到一个需求了,addEventListener默认是事件冒泡,那在我们点击子元素时,由于事件冒泡,父元素上绑定的事件也会一并触发,怎么解决这个问题呢?

我们可以通过stopPropagation()来解决这个需求,它会阻止事件向上或向下传播,相当于让事件传播在此刻停止,但绑定了该方法的事件仍会触发

还是上边的例子,我们在box2上设置stopPropagation()

document.querySelector("#box2").addEventListener("click", (e) => {
console.log("box2触发了");
e.stopPropagation();
});

3

可以看到box2上的事件触发之后,不会再继续向外传播了

  • W3C中,使用stopPropagation()方法
  • IE下设置cancelBubble = true

如果想要阻止事件的默认行为,比如点击<a>标签后的跳转

  • W3c中,使用preventDefault()方法
  • IE下设置window.event.returnValue = false

# 总结

以前,NetscapeMicrosoft是不同的事件传播方式

  • Netscape中,div先触发,也就是事件捕获先触发
  • Microsoft中,p先触发,也就是事件冒泡先触发

两种事件处理顺序刚好相反。IE只支持事件冒泡,Mozilla, Opera 7Konqueror两种都支持事件流,旧版本的 Opera'siCab两种都不支持

现在,W3C规范统一了这个顺序,任何事件发生时,先从顶层开始进行事件捕获,直到事件触发到达了事件源元素。然后,再从事件源往上进行事件冒泡,直到到达document

这也对应了事件流的三个阶段,先是事件捕获阶段,然后是目标阶段,最后是事件冒泡阶段

最后,还要注意的是,不是所有的事件都能冒泡,比如blur、focus、load、unload等等,(这个也是从别人的文章里摘过来的,在这里就不进一步测试了~ )。

How to love
Lil Wayne