Делегирование событий и вложенные элементы

Источник: «Event delegation and nested elements»
Сегодня я хотел бы поговорить о том, как обрабатывать вложенные элементы с делегированием событий.

Что такое делегирование событий

Делегирование событий — это подход, при котором вы прикрепляете слушателя событий к родительскому элементу, а не к прослушиваемому элементу.

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

Здесь, я прослушиваю все события кликов в документе. Если элемент, вызвавший клик, event.target, не имеет класса .click-me, я вызываю return для раннего завершения функции.

// Слушаем клики по всему window
document.addEventListener('click', function (event) {

// Игнорируем элемент без класса .click-me
if (!event.target.matches('.click-me')) return;

// Выполняем код...

});

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

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

Проблема

Есть одна небольшая проблема с делегированием событий, когда в HTML-коде есть вложенные элементы.

Рассмотрим эту кнопку button с классом .click-me.

<button class="click-me">
<svg aria-label="thumbsup">...</svg>
Like it!
</button>

Здесь, если вы кликните на текст Like it!, JavaScript из нашего предыдущего примера будет работать, как и ожидалось.

Но если вы кликните на svg, метод event.target.matches() вернёт false, и ваш код не запуститься.

Почему? Потому что event.target в этой ситуации — это svg, а не родительский элемент button.

Как это исправить

Это на самом деле удивительно просто.

Если в вашем элементе есть вложенные элементы — используйте метод Element.closest().

Этот метод проверяет, имеет ли вызываемый им элемент родителя с предоставленным селектором. Если он находит совпадение, то возвращает элемент.

// Слушаем клики по всему window
document.addEventListener('click', function (event) {

// Игнорируем элемент без класса .click-me
if (!event.target.closest('.click-me')) return;

// Выполняем код...

});

Теперь слушатель событий будет вести себя так, как ожидалось.

Дополнительные материалы

Предыдущая Статья

Понимание порядка выполнения SQL запроса

Следующая Статья

#[Override] в PHP 8.3