Поскольку присоединение слишком большого количества обработчиков событий к элементу может привести к снижению производительности, в этой статье мы обсудим, как свести к минимуму присоединение.

Постановка задачи. Мы хотим загружать данные новостей, когда пользователь нажимает на ссылку, и отображать их на странице text-section.

Если к тегам привязки прикреплены обработчики событий, мы можем перехватывать каждый щелчок мыши по ссылке и предотвращать поведение по умолчанию, когда страница не обновляется, после чего следует запрос на получение и заполнение данных при получении.

<html>
  <head></head>
  <body>
      <div>
        <span id="news-section">Text area to populate</span>
        <ul class="menu-items">
          <li>
            <a id="arrow-button" href="abc.com?page=1">show more text</a>
          </l1>
          <li>
            <a id="arrow-button" href="abc.com?page=2">show more text</a>
          </li>
          <li>
            <a id="arrow-button" href="abc.com?page=3">show more text</a>
          </li>
         </ul>
      </div>
  </body>
</html>

Обычно мы прикрепляли прослушиватели событий к каждому элементу привязки, чтобы перехватывать событие клика.

var anchorElements = document.querySelectorAll('a');
anchorElements.forEach(el => el.addEventListener("click", populateText);

function populateText(e) {
  e = e || window.event;
  e.preventDefault();
  
  var target = e.target || window.target;
  fetchData(target.href)
    .then(response => response.text())
    .then(renderData);
}

Однако, когда на странице большое количество элементов и к каждому из них прикреплен один или несколько обработчиков событий, это приведет к падению производительности — либо в виде увеличения времени на загрузку большего количества разметки и кода Javascript, либо в виде форма более длительного времени выполнения.
Узел DOM, к которому нам нужно получить доступ и изменить, тем медленнее приложение, особенно потому, что фаза присоединения события обычно происходит во время события onloadили DOMContentReady, что является временем занятости для обработки событий. Присоединение обработчиков событий требует времени обработки, а также памяти для отслеживания каждого обработчика.
Много раз большое количество этих обработчиков событий может вообще не понадобиться. Поскольку пользователь будет щелкать по одной ссылке за раз, а не по всем сотням, мы можем оптимизировать это дальше.
Одна вещь, которую мы знаем наверняка, это то, что событие click после достижения цели будет всплывать в дереве DOM к его последовательным элементам-предкам, пока не достигнет window. Таким образом, мы можем обработать это событие в элементе контейнера, который упаковывает все элементы ссылки, а затем проверить все события щелчка, чтобы увидеть, является ли его целью элемент ссылки. Этот метод известен как делегирование событий.

document.querySelector('.menu-items')
  .addEventListener("click", handleClick);

function handleClick(e) {
  e = e || window.event;
  var target = e.target || e.srcElement;
  
  // prevent default behavior and cancel bubbling
  e.preventDefault();
  e.stopPropagation();
  
  // exit the function on non-link clicks
  if (target.nodeName !== 'A') {
    return;
  }
  
  fetchData(target.href)
    .then(response => response.text())
    .then(renderData);
};

Подводя итог использованию делегирования событий, мы:

  1. Обработка подобных событий на уровне контейнера, а не прикрепление обработчиков к каждому целевому элементу.
  2. Проверка необходимости обработки события путем проверки его целевого свойства.
  3. Остановка дальнейшего всплытия события (необязательно).