Закачане на манипулатори на нови елементи в JavaScript

Да предположим, че имаме списък ul и бутон:

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

<button>add</button>

Нека получим нашите елементи в съответните променливи:

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

Нека направим така, че при кликване върху която и да е li да се добавя удивителен знак в края:

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

Сега нека направим така, че при натискане на бутона в края на списъка да се добавя нов li:

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

Обаче ще срещнем проблем: кликването върху новодобавения li няма да доведе до добавяне на удивителен знак в края. Факт е, че ние добавяме манипулатор за кликване само за тези li, които са съществували първоначално, но не и за новите.

Нека поправим проблема, като сложим манипулатор за кликване на новия li :

javascriptbutton.addEventListener('click', function() {
  let item = document.createElement('li');
  item.textContent = 'item';
  
  item.addEventListener('click', function() { // манипулатор за кликване
    this.textContent = this.textContent + '!';
  });
  
  list.appendChild(item);
});

Сега обаче кодът на функцията-манипулатор се дублира на две места - за първоначално съществуващите li и за новите. Нека поправим това, като я изнесем в отделна функция:

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

Нека използваме нашата функция, за да избегнем дублирането на кода:

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);
});

Задачата като цяло е решена и избегнахме дублирането на кода на функцията-манипулатор. Въпреки това, да закачаме манипулатори на събития все пак трябва на две места: както в цикъла за съществуващите li, така и при кликване на бутона. В следващия урок ще разгледаме начин да се отървем от това неудобство.