如何将事件监听器绑定到JavaScript中动态创建的元素上

890 阅读2分钟

你可能已经注意到,你动态创建的新元素不会自动拥有它的同行所拥有的事件监听器。在这篇文章中,我们将利用事件冒泡来确保我们不会遇到这个问题。

一个问题实例

假设我们有一个按钮列表,当我们点击其中一个按钮时,它将提示自己的文本。在这个例子中,我们将从DOM中的两个按钮开始。它们的类是alert-button ,我们将使用这个类来添加提示功能。

<div id="alert-container">
  <button class="alert-button">Button 1</button>
  <button class="alert-button">Button 2</button>
</div>

<script>
  const btns = document.querySelectorAll('.alert-button');
  for (let i = 0; i < btns.length; i++) {
    btns[i].addEventListener('click', function (e) {
      alert(e.target.innerHTML);
    });
  }
</script>

当然,这是可行的。

但如果我们现在需要动态地添加第三个按钮呢?我们可以看到,我们新添加的按钮不起作用,即使我们给它的类与其他按钮相同。

<div id="alert-container">
  <button class="alert-button">Button 1</button>
  <button class="alert-button">Button 2</button>
</div>

<script>
  const btns = document.querySelectorAll('.alert-button');
  for (let i = 0; i < btns.length; i++) {
    btns[i].addEventListener('click', function (e) {
      alert(e.target.innerHTML);
    });
  }

  // Add a new button dynamically
  const newBtn = document.createElement('button');
  newBtn.classList.add('alert-button');
  newBtn.innerHTML = 'Button 3';
  const container = document.querySelector('#alert-container');
  container.append(newBtn);
</script>

而这个新的按钮什么也没做!

事件监听器如何工作

一旦你理解了事件监听器的工作原理,这实际上是预期的行为。当我们最初为我们的两个按钮添加事件监听器时,我们使用它们的类名来选择这些按钮。然而,在我们选择了这些元素之后,这些类名就真的与此事没有任何关系了。如果认为我们的点击甚至与类本身有某种联系,那是一种误解。

换句话说:添加一个相同类别的新元素肯定不会使该元素也有一个事件处理程序!我们需要找到另一种方法。我们需要找到另一种方法。

一个合理的解决方案。事件冒泡

我们的直觉可能是,每当我们添加一个新的元素,我们就应该去为这个元素明确地添加一个点击处理程序。但是,有一个更简单的方法。我们可以使用事件冒泡法。我们可以将事件监听器添加到DOM层次结构中较高的一个元素上(在本例中,#alert-container div)。

当然,我们不希望整个div都触发alert ,只是按钮。对此有一个简单的解决方法:我们只需添加一个测试,以确保只有当被点击的元素的类别为alert-button ,才会执行alert

让我们看看这段代码的样子。

<div id="alert-container">
  <button class="alert-button">Button 1</button>
  <button class="alert-button">Button 2</button>
</div>

<script>
  const container = document.querySelector('#alert-container');

  // Click handler for entire DIV container
  container.addEventListener('click', function (e) {
    // But only alert for elements that have an alert-button class
    if (e.target.classList.contains('alert-button')) {
      alert(e.target.innerHTML);
    }
  });

  const newBtn = document.createElement('button');
  newBtn.classList.add('alert-button');
  newBtn.innerHTML = 'Button 3';
  container.append(newBtn);
</script>

我们可以看到我们为整个容器添加了一个事件监听器,并且我们只在e.target.classList 包含alert-button 时发出警报。这很有效,我们可以在烟雾测试中看到它。

结论

如果我们知道我们将有动态创建的带有事件处理程序的元素,考虑利用事件冒泡来捕获DOM树上的那些事件。这是一个很好的方法,可以确保每个元素都有事件处理程序,而不必担心在新创建的元素中添加处理程序。