Gắn trình xử lý sự kiện cho các phần tử mới trong JavaScript

Giả sử chúng ta có danh sách ul và một nút bấm:

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

<button>add</button>

Lấy các phần tử của chúng ta vào các biến tương ứng:

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

Hãy làm sao cho khi nhấp vào bất kỳ li nào một dấu chấm than sẽ được thêm vào cuối nó:

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

Bây giờ hãy làm sao cho khi nhấn vào nút, một phần tử li mới sẽ được thêm vào cuối danh sách:

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

Tuy nhiên, chúng ta sẽ gặp phải vấn đề: nhấp vào phần tử li mới được thêm vào sẽ không dẫn đến việc thêm dấu chấm than vào cuối. Vấn đề là chúng ta chỉ thêm trình xử lý sự kiện nhấp chuột vào những li đã tồn tại ban đầu, nhưng không phải vào những phần tử mới.

Hãy sửa vấn đề này bằng cách gắn trình xử lý sự kiện nhấp chuột vào phần tử li mới:

javascriptbutton.addEventListener('click', function() {
  let item = document.createElement('li');
  item.textContent = 'item';
  
  item.addEventListener('click', function() { // trình xử lý sự kiện nhấp chuột
    this.textContent = this.textContent + '!';
  });
  
  list.appendChild(item);
});

Tuy nhiên, bây giờ mã của hàm xử lý bị trùng lặp ở hai nơi - cho các phần tử li tồn tại ban đầu và cho các phần tử mới. Hãy sửa điều này bằng cách tách nó ra thành một hàm riêng biệt:

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

Sử dụng hàm của chúng ta để tránh trùng lặp mã:

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

Vấn đề nhìn chung đã được giải quyết và chúng ta đã tránh được việc trùng lặp mã của hàm xử lý. Tuy nhiên, việc gắn các trình xử lý sự kiện vẫn phải được thực hiện ở hai nơi: cả trong vòng lặp cho các phần tử li hiện có, và khi nhấp vào nút. Trong bài học tiếp theo, chúng ta sẽ phân tích cách để loại bỏ sự bất tiện này.