LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 269|回复: 0

事件委托

[复制链接]
发表于 2024-1-22 23:37:58 | 显示全部楼层 |阅读模式
捕获和冒泡允许我们实现最强大的事件处理模式之一,即 事件委托 模式。

这个想法是,如果我们有许多以类似方式处理的元素,那么就不必为每个元素分配一个处理程序 —— 而是将单个处理程序放在它们的共同祖先上。

在处理程序中,我们获取 event.target 以查看事件实际发生的位置并进行处理。

其 HTML 如下所示:

<table>
  <tr>
    <th colspan="3"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>
  </tr>
  <tr>
    <td class="nw"><strong>Northwest</strong><br>Metal<br>Silver<br>Elders</td>
    <td class="n">...</td>
    <td class="ne">...</td>
  </tr>
  <tr>...2 more lines of this kind...</tr>
  <tr>...2 more lines of this kind...</tr>
</table>
该表格有 9 个单元格(cell),但可以有 99 个或 9999 个单元格,这都不重要。

我们的任务是在点击时高亮显示被点击的单元格 <td>。

与其为每个 <td>(可能有很多)分配一个 onclick 处理程序 —— 我们可以在 <table> 元素上设置一个“捕获所有”的处理程序。

它将使用 event.target 来获取点击的元素并高亮显示它。

代码如下:

let selectedTd;

table.onclick = function(event) {
  let target = event.target; // 在哪里点击的?

  if (target.tagName != 'TD') return; // 不在 TD 上?那么我们就不会在意

  highlight(target); // 高亮显示它
};

function highlight(td) {
  if (selectedTd) { // 移除现有的高亮显示,如果有的话
    selectedTd.classList.remove('highlight');
  }
  selectedTd = td;
  selectedTd.classList.add('highlight'); // 高亮显示新的 td
}
此代码不会关心在表格中有多少个单元格。我们可以随时动态添加/移除 <td>,高亮显示仍然有效。

尽管如此,但还是存在缺陷。

点击可能不是发生在 <td> 上,而是发生在其内部。

在我们的例子中,如果我们看一下 HTML 内部,我们可以看到 <td> 内还有嵌套的标签,例如 <strong>:

<td>
  <strong>Northwest</strong>
  ...
</td>
自然地,如果在该 <strong> 上点击,那么它将成为 event.target 的值。


在处理程序 table.onclick 中,我们应该接受这样的 event.target,并确定该点击是否在 <td> 内。

下面是改进后的代码:

table.onclick = function(event) {
  let td = event.target.closest('td'); // (1)

  if (!td) return; // (2)

  if (!table.contains(td)) return; // (3)

  highlight(td); // (4)
};
解释:

elem.closest(selector) 方法返回与 selector 匹配的最近的祖先。在我们的例子中,我们从源元素开始向上寻找 <td>。
如果 event.target 不在任何 <td> 中,那么调用将立即返回,因为这里没有什么事儿可做。
对于嵌套的表格,event.target 可能是一个 <td>,但位于当前表格之外。因此我们需要检查它是否是 我们的表格中的 <td>。
如果是的话,就高亮显示它。
最终,我们得到了一个快速、高效的用于高亮显示的代码,该代码与表格中的 <td> 的数量无关。

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表