js中的事件冒泡、事件捕获、事件委托

事件流

JavaScript和HTML之间的交互是通过事件实现的。事件,就是文档或浏览器窗口发生的一些特定的交互瞬间。可以使用监听器(或事件处理程序)来预定事件,以便事件发生时执行相应的代码。通俗的说,这种模型其实就是一个观察者模式。(事件是对象主题,而这一个个的监听器就是一个个观察者)。

DOM事件流(event flow )存在三个阶段:

  • 事件捕获阶段
  • 处于目标阶段
  • 事件冒泡阶段

Dom标准事件流的触发的先后顺序为:

  • 先捕获再冒泡。即当触发dom事件时,会先进行事件捕获,捕获到事件源之后通过事件传播进行事件冒泡。

addEventListener的第三个参数
在我们平常用的addEventListener方法中,一般只会用到两个参数,一个是需要绑定的事件,另一个是触发事件后要执行的函数,然而addEventListener还可以传入第三个参数:

1
element.addEventListener(event, function, useCapture);

第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数;

如果参数为true,则表示在事件捕获阶段调用处理函数。如果不写第三个参数则默认在事件冒泡阶段调用事件处理函数。

事件冒泡

当一个元素接收到事件的时候,会把他接收到的事件传给自己的父级,一直到 window (注意这里传递的仅仅是事件,例如click、focus等等这些事件, 并不传递所绑定的事件函数。)

事件源 =>根节点(由内到外)进行事件传播。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="outer" style="border: 2px solid red; padding: 20px;">
<p id="middle" style="border: 2px solid green; padding: 20px;">
<button id="inner" style="border: 2px solid blue; padding: 20px;">点击我触发事件</button>
</p>
</div>

// 获取元素
var outer = document.getElementById('outer');
var middle = document.getElementById('middle');
var inner = document.getElementById('inner');

// 为每个元素添加点击事件监听器
outer.addEventListener('click', function() {
console.log('外层元素被点击');
});

middle.addEventListener('click', function() {
console.log('中间元素被点击');
});

inner.addEventListener('click', function() {
console.log('内部元素(按钮)被点击');
});

结果:

内部元素(按钮)被点击
中间元素被点击
外层元素被点击

如何取消事件冒泡?

  • stopPropagation() 方法防止调用相同事件的传播。

  • 传播意味着向上冒泡到父元素或向下捕获到子元素。

可以 在子事件中加入e.stopPropagation() 取消冒泡:

1
2
3
4
5
middle.addEventListener('click', function(event) {
console.log('中间元素被点击');
// 取消事件冒泡
event.stopPropagation();
});

事件捕获

当鼠标点击或者触发dom事件时(被触发dom事件的这个元素被叫作事件源),浏览器会从根节点 =>事件源(由外到内)进行事件传播。

事件捕获与事件冒泡是比较类似的,最大的不同在于事件传播的方向。

事件委托

事件委托也称为事件代理。就是利用事件冒泡,把子元素的事件都绑定到父元素上。如果子元素阻止了事件冒泡,那么委托就无法实现。

给父元素添加事件,点击子元素则冒泡到父元素的事件上,这样就不用一一给子元素绑定事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<body>
<ul id="parentList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>

<script>
// 获取父元素
var parentList = document.getElementById('parentList');

// 添加点击事件监听器到父元素
parentList.addEventListener('click', function (event) {
console.log(event);
// 检查点击的是否是li元素
if (event.target.tagName === 'LI') {
// 输出点击的li元素的文本内容
console.log('点击了列表项:', event.target.textContent);
}
});
</script>
</body>

优点:

  • 提高性能:每一个函数都会占用内存空间,只需添加一个事件处理程序代理所有事件,所占用的内存空间更少。
  • 动态监听:使用事件委托可以自动绑定动态添加的元素,即新增的节点不需要主动添加也可以一样具有和其他元素一样的事件。