当使用者与浏览器互动时,会触发各类不同的事件 (event),例如常见的点击 (click)、滑动 (scroll)。我们可以通过 JavaScript 的事件监听器 (handler),来处理这些事件。让我们能在事件触发时,做出我们要的效果,例如点击某个按钮,触发某个逻辑。
事件委派是当我们想要在一群子元素中,都加上同样的事件监听器时可以派上用场。当我们有许多相同元素,有相似的行为时,我们可以不用在每个元素都加上事件监听器,而是可以直接在父层加上监听器。这时通过 event.target
来得知实际上是哪一个元素发生事件,并处理该事件。
这种把监听器装在父层,然后委派给子元素,就是所谓的事件委派。这么做的好处是,我们不用在每个元素,例如每个按钮上都加上监听器,这可以减少内存消耗;这也让我们的架构更弹性,可以随时新增或移除元素。也可以写比较少的代码,让可阅读性提升。
举例来说 (编按: 此例子来自 MDN),如果想要在一长串列表中的每一项,都加上事件监听,我们可以直接加在父层,不用每个子元素都加上,就算有上百上千个子元素也是。
<div id="container">
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
</div>;
const container = document.querySelector("#container");
container.addEventListener(
"click",
(event) => (event.target.style.backgroundColor = bgChange())
);
事件委派之所以能够发生,是因为在背后的事件捕获与冒泡机制。一般来说,当事件触发时,会先进入捕获阶段,然后到达事件目标,接着才是冒泡阶段。
从上图可得知,所谓的捕获阶段是指,当某个事件触发时,例如使用者点了某个按钮,此时由 DOM 树的最上层 Window 一路往下,将事件传递下去并执行。实际在代码上,需要在事件监听器中,加入 {capture: true}
来开启捕获机制。
冒泡阶段则是比较常用的,跟捕获阶段相反,它是先在目标上执行事件监听器,接着传递到父层,再传到父层的父层,然后一路传上去。
<form onclick="alert('form 点击事件触发')">
这是一个 form 元素
<div onclick="alert('div 点击事件触发')">
这是一个 div 元素
<p onclick="alert('p 点击事件触发')">这是一个 p 元素</p>
</div>
</form>
以上面的例子来说 (建议在面试时也可以简单快速手写这个例子,可以帮助说明),当我们在子层 <p>
装一个 onclick
的监听器,点下去时,不仅该元素有跑出 alert
,其父层 <div>
的 onclick
也被触发,然后父层的父层 <form>
的 onclick
也接续被触发。
event.target
与 event.currentTarget
的差别前面提到事件委派时,我们提到了 event.target
这个属性。而与这个属性相似的是 event.currentTarget
。,但它们之间存在一些重要的差异。
让我们更深入地探讨 event.target
和 event.currentTarget
之间的差异:
event.target
:event.target
的值保持不变event.target
将始终指向该按钮元素event.currentTarget
:event.currentTarget
的值可能会发生变化event.currentTarget
将指向父元素根据具体的需求,我们可以选择使用 event.target
或 event.currentTarget
来实现不同的功能。如果我们想要知道事件的真正来源,并对其进行操作,可以使用 event.target
。如果我们想要在事件冒泡或捕获过程中对当前处理事件的元素进行操作,可以使用 event.currentTarget
。
event.stopPropagation()
在实务上,我们有时候不想要冒泡,例如只想要子元素的事件被触发,不想要父层的元素被触发,避免干扰。这时候想要不发生冒泡,可以在监听器加上 event.stopPropagation()
,不过这个仍会让该监听器执行,只是不会冒泡上去;如果连该监听器的其他事件类别都不想执行的话,可以用 event.stopImmediatePropagation()
。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。