JavaScript中事件委托的介绍及应用

852 阅读4分钟

对JavaScript中事件委托的简单解释

1.为什么要进行事件委托?

让我们在一个HTML按钮被点击时向控制台记录一条信息。

为了实现这个目标,你需要选择按钮,然后使用addEventListener() 方法来附加一个事件监听器。

<button id="buttonId">Click me</button>
<script>
  document.getElementById('buttonId')
    .addEventListener('click', () => console.log('Clicked!'));
</script>

这是监听单个元素(尤其是按钮)事件的方法。

那么,如何监听多个按钮的事件呢?这里有一个可能的实现:

<div id="buttons">
  <button class="buttonClass">Click me</button>
  <button class="buttonClass">Click me</button>
  <!-- buttons... -->
  <button class="buttonClass">Click me</button>
</div>
<script>
  const buttons = document.getElementsByClassName('buttonClass');
  for (const button of buttons) {
    button.addEventListener('click', () => console.log('Clicked!'));
  }
</script>

按钮列表被迭代for (const button of buttons) ,每个按钮都有一个新的监听器。另外,当一个按钮被添加或从列表中移除时,你必须手动移除或附加事件监听器。

有更好的方法吗?

幸运的是,当使用事件委托模式时,监听多个元素的事件只需要一个事件监听器。

事件委托使用的是事件传播机制的具体内容。为了理解事件委托是如何工作的,我建议先了解事件传播的情况。

2.事件传播

当你点击下面HTML中的按钮时:

<html>
  <body>
    <div id="buttons">
      <button class="buttonClass">Click me</button>
    </div>
  </body>
</html>

在多少个元素上会触发一个点击事件?毫无疑问,按钮本身会收到一个点击事件。但同时......所有按钮的祖先,甚至documentwindow 对象。

一个点击事件的传播分为3个阶段:

  1. 捕获阶段--从windowdocument 和根元素开始,事件通过目标元素的祖先向下潜入。
  2. 目标阶段--该事件在用户点击的元素上被触发。
  3. 冒泡阶段--最后,事件通过目标元素的祖先向上冒泡,直到根元素、document 、和window

JavaScript Event Propagation

该方法的第三个参数captureOrOptions

element.addEventListener(eventType, handler[, captureOrOptions]);

可以让你捕捉不同阶段的事件:

  • 如果captureOrOptions 参数缺失,false{ capture: false } ,那么监听器将捕捉目标和冒泡阶段的事件。
  • 如果参数是true{ capture: true } ,那么监听器就会监听捕获阶段的事件。

下面的事件处理程序监听发生在<body> 元素上的捕获阶段的点击事件。

document.body.addEventListener('click', () => {
  console.log('Body click event in capture phase');
}, true);

在这个Codesandbox演示中,当点击按钮时,你可以在控制台看到事件是如何传播的。

好吧,事件传播如何帮助捕捉多个按钮的事件?

算法很简单:将事件监听器附加到按钮的父级,当一个按钮被点击时捕获冒泡的事件。这正是事件委托的工作方式。

3.事件委托

让我们使用事件委托来捕捉多个按钮的点击。

<div id="buttons"> <!-- Step 1 -->
  <button class="buttonClass">Click me</button>
  <button class="buttonClass">Click me</button>
  <!-- buttons... -->
  <button class="buttonClass">Click me</button>
</div>
<script>
  document.getElementById('buttons')
    .addEventListener('click', event => { // Step 2
      if (event.target.className === 'buttonClass') { // Step 3
        console.log('Click!');
      }
    });
</script>

打开Codesandbox演示,点击任何按钮--你会看到'Click!' 消息记录到控制台。

事件委托的想法很简单。你不是直接将事件监听器附加到按钮上,而是监听委托给父级<div id="buttons"> 。当一个按钮被点击时,父元素的监听器就会捕捉到冒泡的事件(还记得事件传播吗)。

使用事件委托需要3个步骤:

步骤1.确定要观察事件的元素的父元素

在上面的例子中,<div id="buttons"> 是按钮的父元素。

第2步。将事件监听器附加到父元素上

document.getElementById('buttons') .addEventListener('click', handler) 将事件监听器附加到按钮的父元素上。这个监听器对按钮的点击做出反应,因为按钮的点击事件会通过祖先冒泡(感谢事件传播)。

第3步。使用event.target来选择目标元素

当一个按钮被点击时,处理函数被调用,其参数是:event 对象。属性event.target 是事件被派发的元素,在这个例子中是一个按钮。

 // ...
  .addEventListener('click', event => {
    if (event.target.className === 'buttonClass') {
      console.log('Click!');
    }
  });

顺便说一句,event.currentTarget 指向事件监听器直接连接的元素。在这个例子中,event.currentTarget 就是<div id="buttons">

现在你可以看到事件委托模式的好处:不需要之前那样给每个按钮附加监听器,由于事件委托,只需要一个事件监听器

4.4.总结

当一个点击事件发生时(或任何其他传播的事件)。

  • 事件从window,document, 根元素向下传播,并通过目标元素的祖先(捕获阶段)。
  • 事件发生在目标上(目标阶段)。
  • 最后,事件通过目标元素的祖先向上冒泡,直到根元素、documentwindow (冒泡阶段)。

这种机制被命名为事件传播

事件委托是一个有用的模式,因为你可以用一个事件处理程序来监听多个元素的事件。

使事件委托工作需要3个步骤:

  1. 确定要监听事件的元素的父级
  2. 将事件监听器附加到父元素上
  3. 使用event.target 来选择目标元素