Attaching handlers to new elements in JavaScript

Suppose we have the list ul and a button:

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

<button>add</button>

Let's get our elements into the corresponding variables:

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

Let's make it so that when you click on any li, an exclamation mark is added to the end of it:

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

Let's now make it so that when the button is pressed, a new li is added to the end of the list:

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

We will however get a problem: clicking on the newly added li will not add an exclamation mark to the end. The point is that we add a click handler only to those li that originally existed, but not to new ones.

Let's fix the problem by attaching a click handler to the new li:

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

However, now the handler function code is duplicated in two places - for the original li and for the new ones. Let's fix this by moving it to a separate function:

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

Use our function to avoid code duplication:

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

The task is generally solved and we have avoided duplicating the code of the handler function. However, we still have to hang event handlers in two places: both in the loop for the existing li, and when the button is clicked. In the next lesson, we will look at a way to get rid of this inconvenience.