Как более эффективно прослушивать событие в разных сценариях
Представим, что в вашем приложении есть несколько различных сценариев, которые должны обнаруживать события click
. Один скрипт открывает модальные окна. Другой переключает некоторые элементы раскрытия/расширения и сворачивания. Третий сохраняет данные формы.
Когда вы прослушиваете одно и то же событие на нескольких элементах, делегирование событий — отличная стратегия!
document.addEventListener('click', function (event) {
// Если объект, по которому кликнули, не является нужным элементом, выходим
if (!event.target.matches('[data-modal="#terms"]')') return;
// Иначе, выполнить код...
});
Но как реализовать делегирование событий, если у вас есть несколько сценариев или отдельных задач, которые зависят от одного и того же события?
Давайте покопаемся!
Подход 1. Несколько слушателей событий
Один из подходов заключается в использовании нескольких слушателей событий, по одному для каждого селектора.
document.addEventListener('click', function (event) {
if (!event.target.matches('[data-modal]')) return;
// Переключение модала...
});
document.addEventListener('click', function (event) {
if (!event.target.matches('[data-disclosure]')) return;
// Показать содержимое раскрытия...
});
document.addEventListener('click', function (event) {
if (!event.target.matches('[data-save-form]')) return;
// Сохранить содержимое формы...
});
Этот подход я использую, когда у меня есть отдельные, разрозненные сценарии, собранные в виде библиотек или самостоятельных функций.
Хотя вы используете несколько слушателей событий вместо одного, это все равно намного лучше для производительности, чем прикрепление слушателя к каждому отдельному элементу соответствия.
Подход 2. Условные выражения
Если обе части кода находятся в одном файле и являются частью одного более широкого сценария, вы можете объединить их в один слушатель событий и использовать условную проверку, например, оператор if...else
или break
.
document.addEventListener('click', function (event) {
if (event.target.matches('[data-modal]')) {
// Переключение модала...
} else if (event.target.matches('[data-disclosure]')) {
// Показать содержание раскрытия...
} else if (event.target.matches('[data-save-form]')) {
// Сохранить содержимое формы...
}
});
Чтобы все было более читабельно, вы можете перенести код, который вы хотите выполнить для каждого условия, в отдельную функцию, а в качестве аргумента передать объект event
или event.target
.
document.addEventListener('click', function (event) {
if (event.target.matches('[data-modal]')) {
toggleModal(event);
} else if (event.target.matches('[data-disclosure]')) {
showDisclosure(event);
} else if (event.target.matches('[data-save-form]')) {
saveForm(event);
}
});
Подход 3. Паттерн раннего возврата
Многие люди считают, что выражения if...else
или break
громоздки, особенно если у вас больше, чем пара проверок. Они могут быстро выйти из-под контроля.
По этой причине я предпочитаю использовать паттерн раннего возврата.
document.addEventListener('click', function (event) {
if (event.target.matches('[data-modal]')) {
toggleModal(event);
return;
}
if (event.target.matches('[data-disclosure]')) {
showDisclosure(event);
return;
}
if (event.target.matches('[data-save-form]')) {
saveForm(event);
return;
}
});
Такой подход, на мой взгляд, более удобен для чтения. Это также означает, что я могу перемещать проверки, не беспокоясь о том, что они что-то сломают.
Подход 4. Ранний возврат в функциях обработчика
Вместо того чтобы использовать кучу операторов if
с ранними возвратами, вы можете запустить каждую функцию-обработчик каждый раз, а ранний возврат перенести внутрь обработчика.
function toggleModal (event) {
if (event.target.matches('[data-modal]')) return;
// Переключение модала...
}
function showDisclosure (event) {
if (event.target.matches('[data-disclosure]')) return;
// Показать содержимое раскрытия...
}
function saveForm (event) {
if (event.target.matches('[data-save-form]')) return;
// Сохранить содержимое формы...
}
document.addEventListener('click', function (event) {
toggleModal(event);
showDisclosure(event);
saveForm(event);
});
Этот подход обеспечивает хороший баланс производительности и удобства обслуживания.
Он немного менее производителен, поскольку showDisclosure()
и saveForm()
все равно будут выполняться, даже если кликнутый элемент был модальным переключателем. Но я не ожидаю каких-либо реальных последствий для производительности.
И я думаю, что это обеспечивает более чистую и читабельную структуру, чем некоторые другие варианты.