[JavaScript, Разработка веб-сайтов] Простое объяснение делегирования событий (перевод)

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
30-Июл-2020 17:35

Приветствую. Представляю вашему вниманию перевод статьи «A Simple Explanation of Event Delegation in JavaScript», опубликованной 14 июля 2020 года автором Dmitri Pavlutin

В данной статье Дмитрий Павлутин объясняет, на чём основан один из базовых паттернов работы с DOM-событиями.
1. Почему делегирование событий?
Давайте напишем скрипт, который при нажатии на HTML-кнопку, будет отправлять сообщение в консоль.
Чтобы срабатывало такое поведение, необходимо в JavaScript найти эту кнопку и с помощью метода addEventListener() прикрепить к ней обработчик события.
<button id="buttonId">Click me</button>
<script>
  document.getElementById('buttonId')
    .addEventListener('click', () => console.log('Clicked!'));
</script>

Данный способ позволяет начать отслеживать события на одном элементе. Например, на кнопке.
А что, если возникает необходимость отслеживать события на множестве кнопок? Вот пример одного из способов реализаций:
<div id="buttons">
  <button class="buttonClass">Click me</button>
  <button class="buttonClass">Click me</button>
  <!-- Кнопки... -->
  <button class="buttonClass">Click me</button>
</div>
<script>
  const buttons = document.getElementsByClassName('buttonClass');
  for (const button of buttons) {
    button.addEventListener('click', () => console.log('Clicked!'));
  }
</script>

Посмотреть, как работает данный способ, можно в демонстрации CodeSandbox
Сначала делается выборка всех необходимых кнопок страницы, затем с помощью цикла for (const button of buttons) производится обход всех элементов этого списка, в котором к каждой кнопке прикрепляется обработчик события. Также, когда во время работы с документом на странице появляются новые кнопки, возникает необходимость вручную прикреплять обработчики событий для этих новых элементов.
Существует ли лучший способ?
К счастью, при использовании шаблона "делегирование событий", отслеживание событий на множестве элементов требует наличия только одного обработчика.
Делегирование событий использует особенности работы "распространения событий". Чтобы понять, как работает делегирование, предлагаю сначала разобраться в принципе работы их распространения.
2. Распространение событий
Когда вы нажимаете на кнопку в следующей HTML-разметке:
<html>
  <body>
    <div id="buttons">
      <button class="buttonClass">Click me</button>
    </div>
  </body>
</html>

На каком количестве элементов сработает событие click? Без сомнений, событие клика получит сама кнопка. Но помимо неё, такое же событие получит и вся цепочка элементов, являющихся её предками (даже объекты document и window).
Событие клика распространяется в 3 этапа:
  • Фаза захвата / погружения (capturing phase) – начиная с window, document и корневого элемента, событие погружается сверху вниз по DOM-дереву через предков целевого элемента, на котором произошло событие
  • Фаза цели (target phase) – срабатывание соыбытия на элементе, на который пользователь кликнул
  • Фаза всплытия (bubble phase) – наконец, событие всплывает по цепочке предков целевого элемента, пока не достигнет корневого элемента, а затем объектов document и window


Третий аргумент captureOrOptions метода addEventListener:
element.addEventListener(eventType, handler[, captureOrOptions]);

позволяет вам перехватывать события на разных этапах их распространения.
  • Если аргумент captureOrOptions пропущен, имеет значение false или `{ capture: false }, обработчик будет захватывать события на "Фазе цели" и "Фазе всплытия"
  • Если же аргумент captureOrOptions имеет значение true или `{ capture: true }, обработчик сработает уже на "Фазе захвата (погружения)"

В следующем примере обработчик перехватывает событие клика на элементе <body> на "Фазе захвата":
document.body.addEventListener('click', () => {
  console.log('Body click event in capture phase');
}, true);

В демонстрации CodeSandbox, при нажатии на кнопку, в консоли можно увидеть, как распространяется событие.
Итак, как распространение события помогает перехватывать события из множества кнопок?
Принцип прост: обработчик события прикрепляется к элементу, являющемуся для кнопок родительским, и при нажатии на кнопку отлавливает всплывающее событие. Именно так работает делегирование событий.
3. Делегирование событий
Давайте воспользуемся делегированием, чтобы отловить клики на несколько кнопок:
<div id="buttons"> <!-- Шаг 1 -->
  <button class="buttonClass">Click me</button>
  <button class="buttonClass">Click me</button>
  <!-- Кнопки... -->
  <button class="buttonClass">Click me</button>
</div>
<script>
  document.getElementById('buttons')
    .addEventListener('click', event => { // Step 2
      if (event.target.className === 'buttonClass') { // Step 3
        console.log('Click!');
      }
    });
</script>

Откройте демонстрационный код и кликните на любую кнопку – вы увидите в консоли сообщение "Click!".
Идея делегирования событий роста. Вместо прикрепления обработчиков событий прямо к кнопкам, мы делегируем отслеживание этого события родительскому элементу <div id="buttons">. Когда нажимается кнопка, обработчик, назначенный родительскому элементу ловит всплывающее событие (помните раздел про распространение событий?).
Использование делегирования событий требует 3 шагов:
Шаг 1. Определить общего родителя элементов для отслеживания событий
В примере ниже <div id="buttons"> является общим родителем для кнопок.
Шаг 2. Прикрепить к родительскому элементу обработчик событий
document.getElementById('buttons').addEventListener('click', handler) прикрепляет обработчик событий к родителю кнопок. Этот обработчик также реагирует на нажатия на кнопки, так как события нажатий на кнопки всплывают по всем элементам-предкам (благодаря распространению событий).
Шаг 3. Использовать event.target для выбора целевого элемента
Когда кнопка нажата, функция-обработчик вызывается с аргументом: объектом event. Свойство event.target обращается к элементу, на котором произошло событие (в нашем примере этот элемент – кнопка):
// ...
  .addEventListener('click', event => {
    if (event.target.className === 'buttonClass') {
      console.log('Click!');
    }
  });

Кстати, на элемент к которому прикреплён сработавший обработчик события, указывает event.currentTarget. В нашем примере event.currentTarget указывает на элемент <div id="buttons">.
Теперь вы можете увидеть преимущества шаблона делегирования событий: вместо прикрепления обработчиков к каждой кнопке, как это было сделано раньше, благодаря делегированию событий, остаётся потребность только в одном обработчике.
4. Резюме
Когда происходит событие нажатия на кнопку (или любое другое распространяющееся событие):
  • Сначала событие опускается вниз от window, document, корневого элемента и через всех предков целевого элемента (фаза захвата / погружения)
  • Событие происходит на целевом элемента (фаза цели)
  • И наконец, событие всплывает вверх через элементы, являющиеся предками, пока не достигнет корневого элемента, document и window (фаза всплытия)

Механизм называется распространением события.
Делегирование событий является полезным шаблоном, так как позволяет отслеживать события на множестве элементов с помощью только одного обработчика.
Для работы делегирования событий нужно 3 шага:
  • Определить родителя элементов для отслеживания событий
  • Прикрепить на него обработчик событий
  • Использовать event.target для выбора целевого элемента

===========
Источник:
habr.com
===========

===========
Автор оригинала: Dmitri Pavlutin
===========
Похожие новости: Теги для поиска: #_javascript, #_razrabotka_vebsajtov (Разработка веб-сайтов), #_javascript, #_web_development, #_frontend, #_razrabotka_vebsajtov (разработка веб-сайтов), #_javascript, #_razrabotka_vebsajtov (
Разработка веб-сайтов
)
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 22-Ноя 12:22
Часовой пояс: UTC + 5