DOM 事件流:捕获、目标与冒泡阶段的深入理解

好的,各位亲爱的开发者们,欢迎来到今天的“DOM事件流:捕获、目标与冒泡阶段的深入理解”特别讲座!我是你们的老朋友,人称“代码界的段子手”——码农老王。今天,咱们不聊八卦,不谈人生,就来聊聊前端世界里那些你可能熟悉,但又可能只是“略懂”的事件流。

准备好了吗?系好安全带,咱们的“代码飞船”即将启航,目标:DOM事件流的星辰大海!🚀

一、开场白:DOM事件流,前端世界的“宫斗剧”?

各位有没有看过宫斗剧?后宫佳丽三千,为了争夺皇上的宠爱,那叫一个“你方唱罢我登场”,各种明争暗斗,好不热闹。其实,咱们的DOM事件流,某种程度上也像一部“前端宫斗剧”。

想象一下,用户在网页上点击了一个按钮,这个小小的点击,就像一颗石子投入平静的湖面,激起层层涟漪。这个“涟漪”就是事件,而事件的传播过程,就是我们的DOM事件流。

那么,在这个“宫斗剧”里,都有哪些“佳丽”争奇斗艳呢?答案就是:捕获阶段、目标阶段和冒泡阶段。它们各自怀揣着不同的“心思”,为了争夺事件的“控制权”,上演了一出精彩纷呈的“前端大戏”。

二、第一幕:捕获阶段——“先下手为强”的策略家

首先登场的是捕获阶段,它就像后宫里那些深谋远虑的“策略家”,信奉“先下手为强”的原则。

当事件发生时,浏览器会从window对象开始,沿着DOM树向下寻找目标元素。这个过程就像“皇上”巡视后宫,从最高处开始,一层一层地往下看,寻找那个“天选之女”。

在这个过程中,任何一个“妃子”(也就是DOM元素),如果设置了事件监听器,并且指定了useCapturetrue,那么它就有机会“截获”事件。

举个例子,咱们写一段简单的HTML代码:

<div id="grandparent">
  <div id="parent">
    <button id="child">点击我</button>
  </div>
</div>

然后,咱们用JavaScript给grandparentchild分别添加一个事件监听器,并且grandparent的监听器设置为捕获阶段:

const grandparent = document.getElementById('grandparent');
const child = document.getElementById('child');

grandparent.addEventListener('click', function(event) {
  console.log('Grandparent (Capture): ' + event.target.id);
}, true); // 注意这里的true,表示捕获阶段

child.addEventListener('click', function(event) {
  console.log('Child (Target): ' + event.target.id);
});

现在,当你点击child按钮时,控制台会先输出:

Grandparent (Capture): child
Child (Target): child

看到了吗?grandparent的事件监听器先于child执行,这就是捕获阶段的“先下手为强”策略。

表格总结:捕获阶段的特点

特点 描述
传播方向 window对象开始,沿着DOM树向下传播
监听器触发条件 元素设置了事件监听器,并且useCapturetrue
执行顺序 在目标阶段和冒泡阶段之前执行
策略 “先下手为强”,可以在事件到达目标元素之前进行处理,例如阻止事件继续传播

三、第二幕:目标阶段——“众星捧月”的焦点

接下来登场的是目标阶段,它就像后宫里被“皇上”选中的“天选之女”,成为了整个事件的焦点。

当事件到达目标元素时,就会进入目标阶段。在这个阶段,目标元素上绑定的事件监听器会被依次触发。

在上面的例子中,child按钮就是目标元素,当点击事件到达child时,child的事件监听器就会被触发。

四、第三幕:冒泡阶段——“母凭子贵”的上位者

最后登场的是冒泡阶段,它就像后宫里那些“母凭子贵”的“上位者”,凭借着与“天选之女”的关系,也能够分一杯羹。

当事件到达目标元素,并且目标元素上的事件监听器执行完毕后,事件会沿着DOM树向上冒泡,依次触发父元素、祖父元素等上面的事件监听器。

继续用上面的例子,在child的事件监听器执行完毕后,事件会冒泡到parentgrandparent,如果它们也设置了事件监听器,那么这些监听器也会被触发。

咱们给parent也添加一个事件监听器:

const parent = document.getElementById('parent');

parent.addEventListener('click', function(event) {
  console.log('Parent (Bubbling): ' + event.target.id);
});

现在,当你点击child按钮时,控制台会输出:

Grandparent (Capture): child
Child (Target): child
Parent (Bubbling): child

看到了吗?parent的事件监听器在child之后执行,这就是冒泡阶段的“母凭子贵”策略。

表格总结:冒泡阶段的特点

特点 描述
传播方向 从目标元素开始,沿着DOM树向上传播
监听器触发条件 元素设置了事件监听器,并且useCapturefalse(默认值)
执行顺序 在目标阶段之后执行
策略 “母凭子贵”,可以利用事件冒泡,将事件处理委托给父元素或祖父元素,减少事件监听器的数量,提高代码的可维护性

五、阻止事件传播:后宫里的“禁足令”

在某些情况下,我们可能不希望事件继续传播,例如,我们希望阻止事件冒泡到父元素或祖父元素。这时候,我们可以使用event.stopPropagation()方法,就像后宫里的“禁足令”,阻止“妃子”们继续争宠。

例如,我们可以在child的事件监听器中调用event.stopPropagation()

child.addEventListener('click', function(event) {
  console.log('Child (Target): ' + event.target.id);
  event.stopPropagation(); // 阻止事件冒泡
});

现在,当你点击child按钮时,控制台只会输出:

Grandparent (Capture): child
Child (Target): child

parent的事件监听器不会被触发,因为事件在child处就被“禁足”了。

六、事件委托:后宫里的“总管太监”

事件委托是一种常用的优化技巧,它可以将事件处理委托给父元素或祖父元素,减少事件监听器的数量,提高代码的可维护性。就像后宫里的“总管太监”,负责管理所有“妃子”的事务。

例如,我们有一个列表,列表里有很多li元素,每个li元素都有一个点击事件。如果我们给每个li元素都添加一个事件监听器,那么代码就会变得很冗余。

<ul id="list">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>
const listItems = document.querySelectorAll('#list li');
listItems.forEach(item => {
  item.addEventListener('click', function(event) {
    console.log('Clicked: ' + event.target.textContent);
  });
});

使用事件委托,我们可以只给ul元素添加一个事件监听器,然后通过event.target来判断点击的是哪个li元素。

const list = document.getElementById('list');
list.addEventListener('click', function(event) {
  if (event.target.tagName === 'LI') {
    console.log('Clicked: ' + event.target.textContent);
  }
});

这样,无论列表里有多少个li元素,我们只需要一个事件监听器,代码就变得简洁多了。

七、总结:DOM事件流,前端世界的“大戏”

好了,各位亲爱的开发者们,今天的“DOM事件流:捕获、目标与冒泡阶段的深入理解”特别讲座就到这里了。

我们一起了解了DOM事件流的三个阶段:捕获阶段、目标阶段和冒泡阶段。我们还学习了如何阻止事件传播,以及如何使用事件委托来优化代码。

希望通过今天的学习,大家能够对DOM事件流有一个更深入的理解,能够在实际开发中灵活运用这些知识,写出更加高效、可维护的代码。

最后,用一句“代码界的鸡汤”来结束今天的讲座:

“代码就像人生,总有起起落落,但只要我们坚持学习,不断进步,终将到达成功的彼岸!”

感谢大家的参与!我们下期再见!👋

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注