Anexando manipuladores a novos elementos em JavaScript

Suponha que temos uma lista ul e um botão:

html<ul>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
</ul>

<button>add</button>

Vamos obter nossos elementos nas variáveis correspondentes:

javascriptlet button = document.querySelector('button');
let list   = document.querySelector('ul');
let items  = list.querySelectorAll('li');

Vamos fazer com que ao clicar em qualquer li um ponto de exclamação seja adicionado ao seu final:

javascriptfor (let item of items) {
  item.addEventListener('click', function() {
    this.textContent = this.textContent + '!';
  });
}

Agora vamos fazer com que ao clicar no botão, um novo li seja adicionado ao final da lista:

javascriptbutton.addEventListener('click', function() {
  let item = document.createElement('li');
  item.textContent = 'item';
  list.appendChild(item);
});

No entanto, teremos um problema: o clique em uma nova li adicionada não resultará na adição de um ponto de exclamação ao final. O fato é que adicionamos o manipulador de clique apenas às li que existiam inicialmente, mas não às novas.

Vamos corrigir o problema anexando à nova li um manipulador de clique:

javascriptbutton.addEventListener('click', function() {
  let item = document.createElement('li');
  item.textContent = 'item';
  
  item.addEventListener('click', function() { // manipulador de clique
    this.textContent = this.textContent + '!';
  });
  
  list.appendChild(item);
});

No entanto, agora o código da função manipuladora está duplicado em dois lugares - para as li existentes inicialmente e para as novas. Vamos corrigir isso extraindo o código para uma função separada:

javascriptfunction handler() {
  this.textContent = this.textContent + '!';
}

Vamos usar nossa função para evitar duplicação de código:

javascriptfor (let item of items) {
  item.addEventListener('click', handler);
}

button.addEventListener('click', function() {
  let item = document.createElement('li');
  item.textContent = 'item';
  
  item.addEventListener('click', handler);
  
  list.appendChild(item);
});

O problema está basicamente resolvido e evitamos a duplicação do código da função manipuladora. No entanto, ainda precisamos anexar os manipuladores de eventos em dois lugares: no loop para as li existentes e no clique do botão. Na próxima lição, veremos uma maneira de nos livrarmos dessa inconveniência.