Делегирование событий и вложенные элементы
Что такое делегирование событий
Делегирование событий — это подход, при котором вы прикрепляете слушателя событий к родительскому элементу, а не к прослушиваемому элементу.
Затем, в вашей функции обратного вызова вы отфильтровываете любые события, происходящие с элементами, которые вам не нужны.
Здесь, я прослушиваю все события кликов в документе. Если элемент, вызвавший клик, 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;
// Выполняем код...
});
Теперь слушатель событий будет вести себя так, как ожидалось.