Как более эффективно прослушивать событие в разных сценариях

Источник: «How to more performantly listen for the same event across different scripts with JavaScript»
Сегодня мы поговорим о том, как управлять слушателями событий, когда у вас есть несколько сценариев, которые слушают одно и то же событие.

Представим, что в вашем приложении есть несколько различных сценариев, которые должны обнаруживать события 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() все равно будут выполняться, даже если кликнутый элемент был модальным переключателем. Но я не ожидаю каких-либо реальных последствий для производительности.

И я думаю, что это обеспечивает более чистую и читабельную структуру, чем некоторые другие варианты.

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

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

Laravel Folio — система маршрутизации на основе файлов и каталогов

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

Подтверждение паролем чувствительных действий в Laravel