简述 DOM 事件模型

168 阅读3分钟

事件流是一个事件沿着特定数据结构传播的过程。冒泡和捕获是事件流在DOM中两种不同的传播方式。

对事件冒泡和捕捉的解释

当一个事件发生在具有父元素的元素上时,现代浏览器运行两个不同的阶段 - 捕获阶段和冒泡阶段。 在捕获阶段:

  • 浏览器检查元素的最外层祖先<html>,是否在捕获阶段中注册了一个onclick事件处理程序,如果是,则运行它。
  • 然后,它移动到<html>中单击元素的下一个祖先元素,并执行相同的操作,然后是单击元素再下一个祖先元素,依此类推,直到到达实际点击的元素。

在冒泡阶段,恰恰相反:

  • 浏览器检查实际点击的元素是否在冒泡阶段中注册了一个onclick事件处理程序,如果是,则运行它
  • 然后它移动到下一个直接的祖先元素,并做同样的事情,然后是下一个,等等,直到它到达<html>元素。

bubbling-capturing.png

videoBox.onclick = function() {
  videoBox.setAttribute('class', 'hidden');
};

video.onclick = function() {
  video.play();
};

在现代浏览器中,默认情况下,所有事件处理程序都在冒泡阶段进行注册。因此,在当前的示例中,当单击视频时,这个单击事件从<video>元素向外冒泡直到<html>元素。沿着这个事件冒泡线路:

  • 它发现了video.onclick...事件处理器并且运行它,因此这个视频<video>第一次开始播放。
  • 接着它发现了(往外冒泡找到的) videoBox.onclick...事件处理器并且运行它,因此这个视频<video>也隐藏起来了。

用 stopPropagation() 修复问题

这是令人讨厌的行为,但有一种方法来解决它!标准事件对象具有可用的名为 stopPropagation()的函数, 当在事件对象上调用该函数时,它只会让当前事件处理程序运行,但事件不会在冒泡链上进一步扩大,因此将不会有更多事件处理器被运行(不会向上冒泡)。所以,可以通过改变处理函数来解决当前的问题:

video.onclick = function(e) {
  e.stopPropagation();
  video.play();
};

注意:

默认情况下,所有事件处理程序都是在冒泡阶段注册的,这在大多数情况下更有意义。如果真的想在捕获阶段注册一个事件,那么可以通过使用addEventListener()注册处理程序,并将可选的第三个属性设置为true。

事件委托

冒泡还允许我们利用事件委托——这个概念依赖于这样一个事实,如果想要在大量子元素中单击任何一个都可以运行一段代码,可以将事件监听器设置在其父节点上,并让子节点上发生的事件冒泡到父节点上,而不是每个子节点单独设置事件监听器。

一个很好的例子是一系列列表项,如果想让每个列表项被点击时弹出一条信息,可以将click单击事件监听器设置在父元素<ul>上,这样事件就会从列表项冒泡到其父元素<ul>上。

<ul id="parent-list">
	<li id="post-1">Item 1</li>
	<li id="post-2">Item 2</li>
	<li id="post-3">Item 3</li>
	<li id="post-4">Item 4</li>
	<li id="post-5">Item 5</li>
	<li id="post-6">Item 6</li>
</ul>
// 获取元素并添加点击事件...
document.getElementById("parent-list").addEventListener("click", function(e) {
	// e.target 是被点击的元素
	// 如果它是一个列表项
	if(e.target && e.target.nodeName == "LI") {
		console.log("List item ", e.target.id.replace("post-", ""), " was clicked!");
	}
});

也可以通过 Element.matches() 来过滤:

document.getElementById("myDiv").addEventListener("click",function(e) {
  if (e.target && e.target.matches("a.classA")) {
    console.log("Anchor element clicked!");
	}
});

参考自:

  1. 事件介绍
  2. How JavaScript Event Delegation Works