Делегирование событий и множественные селекторы с ванильным JS
Сегодня мы рассмотрим подходы к использованию делегирования событий с несколькими селекторами.
Как это работает
Если вы не знакомы с подходом, вот как это работает. Вместо того, чтобы прикреплять события к определенным элементам, вы прослушиваете это событие в родительском элементе (часто это document
или window
).
Любое событие такого типа, которое происходит внутри родительского элемента, всплывает, и вы можете отфильтровать те, которые не соответствуют элементу, который вы ищете.
document.addEventListener('click', function (event) { if (!event.target.matches('.click-me')) return; });
Если вы прослушиваете события для более чем одного элемента с одним и тем же селектором, этот подход на самом деле лучше для производительности, поскольку он использует меньше памяти в браузере.
Несколько селекторов
Когда я делюсь этим подходом, у меня часто есть студенты, спрашивающие меня, как реализовать его, когда вам нужно прослушать событие одного и того же типа для нескольких разных селекторов, и делать разные вещи в зависимости от того, какой селектор имеет элемент.
Например, предположим, что у вас есть кнопки отображения / скрытия содержимого, на которых есть класс .show-me
, и другой набор кнопок с классом .save
, которые сохраняют содержимое формы для повторного использования позже.
Как вы анализируете, какие действия запускать на основе селектора?
Вариант 1: отдельные слушатели событий
Наиболее очевидный подход заключается в использовании двух отдельных слушителей событий, по одному для каждого селектора.
document.addEventListener('click', function (event) { if (!event.target.matches('.show-me')) return; }); document.addEventListener('click', function (event) { if (!event.target.matches('.save')) return; });
Этот подход хорошо работает, если ваши скрипты хранятся в отдельных модульных файлах.
Хотя вы используете два слушителя событий вместо одного, это все же намного лучше для производительности, чем присоединение слушателя к каждой соответствующей кнопке.
Вариант 2: if...else
Если оба фрагмента кода находятся в одном файле, вы можете объединить их в один слушитель событий и использовать оператор if...else if
.
document.addEventListener('click', function (event) { if (event.target.matches('.show-me')) { // ... } else if (event.target.matches('.save')) { // ... } });
Это дает вам небольшой прирост производительности.
Вариант 3: if
и return
Многие люди считают if...else if
громоздкими, особенно если у вас есть больше, чем просто пара проверок. Они могут быстро вырасти и выйти из-под контроля.
По этой причине я предпочитаю использовать последовательные операторы if
с return
для завершения функции при совпадении.
document.addEventListener('click', function (event) { if (event.target.matches('.show-me')) { // ... return; } if (event.target.matches('.save')) { // ... return; } });
Этот подход для меня легче читать. Это также означает, что я могу перемещать свои проверки, не беспокоясь о том, чтобы что-нибудь сломать.
Вариант 4: функции обработчика
Если вам нравится функциональное программирование , этот подход обеспечивает хорошую структуру.
Внутри обратного вызова слушателя событий вы вызываете отдельные функции-обработчики для каждого селектора и передаете объект event
. В каждой функции-обработчике вы запускаете проверку селектора и запускаете код соответственно.
var showMe = function (event) { if (!event.target.matches('.show-me')) return; // ... }; var saveMe = function (event) { if (!event.target.matches('.save')) return; // ... }; document.addEventListener('click', function (event) { showMe(event); saveMe(event); });
Такой подход обеспечивает хороший баланс производительности и ремонтопригодности.
Это немного менее производительней, чем варианты 2 и 3, потому что функция saveMe()
будет работать, даже если showMe()
найдет совпадение (два других подхода останавливаются, как только найдено совпадение). Но это все же лучше, чем иметь отдельных слушателей событий, и обеспечивает чистую, читаемую структуру кода.