Улучшение производительности с делегированием событий

Источник: «Enhancing Web Performance with Event Delegation»
Делегирование событий — техника в веб-разработке, используемая для более эффективного управления событиями. Вместо того чтобы добавлять слушателей событий к каждому элементу, можно прикрепить один слушатель к родительскому элементу, повысив эффективность. Этот родительский элемент затем обрабатывает события для всех дочерних элементов и становится более эффективным, как продемонстрировано в статье.

Представьте веб-страницу с несколькими интерактивными кнопками. Обработка события click для каждой кнопки отдельно может быть громоздкой и неэффективной. Как разработчикам упростить этот процесс и сохранить производительность? Ответ кроется в делегировании событий JavaScript.

Этот метод особенно удобен при работе с динамическим контентом, когда новые элементы появляются после загрузки страницы. Такой подход упрощает код и облегчает управление динамическими изменениями в DOM.

Преимущества делегирования событий

Делегирование событий имеет ряд преимуществ, повышающих как производительность, так и удобство работы с кодом. Ниже приведены ключевые преимущества:

Эти реальные сценарии подчёркивают универсальность и эффективность делегирования событий, делая его незаменимым инструментом в современной веб-разработке и обеспечивающим чистоту кода.

Концепция всплытия события (Event Bubbling)

Всплытия события/Event Bubbling — это важная концепция, с которой необходимо ознакомиться, прежде чем изучать, как работает делегирование событий. Это позволит понять, как работают события в DOM. Когда событие срабатывает на элементе, оно не заканчивается в этой точке. Вместо того чтобы остановиться на этом, оно всплывает, перемещаясь от целевого элемента к его родительским элементам, а затем к следующему родительскому элементу. Эта схема продолжается до тех пор, пока не достигнет корневого элемента.

Например, если кликнуть на button внутри div, событие click начнётся на button, а затем переместится на div. Делегирование событий использует это явление, позволяя разработчикам более эффективно управлять событиями. Прикрепляя один слушатель событий только к одному родительскому элементу, можно обрабатывать события для нескольких элементов с помощью одного слушателя и повысить эффективность работы без использования нескольких слушателей событий.

Как события распространяются по дереву DOM

Распространение событий относится к тому, как информация перемещается по древовидной структуре веб-страницы после выполнения какого-либо действия. Это движение может происходить либо вверх от целевого элемента к его предкам, либо вниз от документа верхнего уровня к целевому элементу. В распространении событий есть три фазы:

Всплытие событий — наиболее часто используемый этап делегирования событий. Понимая, как распространяются события, разработчики могут эффективно управлять слушателями событий и обрабатывать события на разных уровнях дерева DOM.

Всплытие событий в действии

В этом разделе на практическом примере можно наглядно увидеть, как работает всплытие событий. Чтобы увидеть, как работает всплытие событий в действии, рассмотрим простой пример с вложенными элементами HTML:

<div id="parent">
<button id="child">Click Me!</button>
</div>

В примере, приведённом выше, есть элементы div и button. Если сделать так, чтобы что-то происходило при клике на div и при клике на кнопку внутри этого div, то сначала будет кликнута кнопка. Но поскольку button находится внутри div, событие click будет вызывать действия как для button, так и для div, по мере того как событие будет распространяться по DOM.

document.getElementById('parent').addEventListener('click', function(event) {
console.log('Parent clicked');
});

document.getElementById('child').addEventListener('click', function(event) {
console.log('Child clicked');
});

При клике на кнопку в консоли отобразится следующее

Child clicked
Parent clicked

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

Делегирование событий подразумевает передачу обработки событий от отдельных дочерних элементов общему предку (обычно родителю или контейнеру более высокого уровня). Это позволяет более эффективно управлять событиями и не прикреплять слушателей к каждому элементу. При этом слушатель событий прикрепляется к родительскому элементу, а не к каждому дочернему элементу в отдельности. Ключевое преимущество делегирования событий — эффективность.

Примеры кода, иллюстрирующие подход

Предположим, есть список элементов, которым нужно назначить слушателя события click; можно просто добавить слушателя события click только к родительскому элементу (ul) вместо того, чтобы добавлять слушателей события к каждому из элементов (li).

Рассмотрим приведённый ниже код:

<!-- HTML структура: -->
<ul id="item-list">
<li class="item"> Item 1 </li>
<li class="item"> Item 2 </li>
<li class="item"> Item 3 </li>
</ul>

И JavaScript код реализующий делегирования событий:

document.getElementById('item-list').addEventListener('click', function(event) {
// Проверяем, является ли кликнутый элемент элементом списка
if (event.target && event.target.matches('li.item')) {
console.log('You clicked on', event.target.textContent);
}
});

Внутри функции слушателя события проверяем, совпадает ли цель события (элемент, на который был совершён клик) с селектором li.item. Если совпадает, то событие обрабатывается, и текстовое содержимое кликнутого элемента выводится в консоль.

Вот что происходит при клике на Item 2:

You clicked on Item 2

Родительский элемент ul перехватывает событие, но оно обрабатывается только в том случае, если элемент, по которому кликнули, является одним из элементов списка (li.item). Такой эффективный подход избавляет от необходимости прикреплять отдельные слушатели событий к каждому элементу списка.

Обработка динамических элементов

Одним из существенных преимуществ делегирования событий является автоматическая обработка динамических элементов. Если добавить новые элементы списка в элемент ul после загрузки страницы, тот же самый слушатель событий будет перехватывать и обрабатывать события click для новых элементов без какого-либо дополнительного кода.

Например, если добавить новый элемент динамически:

const newItem = document.createElement('li');
newItem.textContent = 'Item 4';
newItem.classList.add('item');
document.getElementById('item-list').appendChild(newItem);

При клике на Item 4 будет срабатывать тот же обработчик события:

You clicked on Item 4

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

Потенциальные недостатки делегирования событий

Хотя делегирование событий даёт значительные преимущества с точки зрения производительности и простоты кода, важно осознавать потенциальные недостатки, возникающие при использовании этой техники. Ниже перечислены некоторые из них:

Снижение производительности

Несмотря на то что делегирование событий позволяет сократить количество слушателей событий, оно может привести к снижению производительности при работе с большими коллекциями дочерних элементов. Слушатель событий родительского элемента должен определить, какой дочерний элемент вызвал событие, что предполагает проверку event.target или соответствующих селекторов. Этот процесс может снизить производительность, особенно в сценариях с тысячами дочерних элементов.

Рекомендации: В таких случаях может помочь оптимизация логики делегирования событий путём сужения условий обработки событий. Например, можно добавить определённые условия, чтобы свести к минимуму ненужные проверки.

Обработка пустых списков или контейнеров

Следует обратить внимание на ситуацию, когда родительский элемент (например, ul) не имеет дочерних элементов (например, элементов li). В этом случае слушатель событий всё равно будет подключён, но при отсутствии дочерних элементов может не произойти никаких значимых действий. Это может привести к ненужной обработке событий при кликах на пустых областях.

Решение: Чтобы решить эту проблему, рассмотрите возможность включения проверок (например, операторов if) в обработчик событий, чтобы проверить наличие дочерних элементов перед выполнением каких-либо действий. Например, убедитесь, что event.target соответствует ожидаемому дочернему селектору, прежде чем приступать к логике обработки события. Кроме того, можно динамически добавлять или удалять обработчик событий в зависимости от наличия дочерних элементов.

Сложная логика обработки событий

Делегирование событий может стать сложным при работе со сложными структурами DOM или когда дочерние элементы имеют разное поведение. Например, если есть несколько типов дочерних элементов в родительском контейнере, код делегирования событий может потребовать учёта различных сценариев, что делает логику более сложной и трудноуправляемой.

Решение: Если сложность логики делегирования событий значительно возрастает, возможно, лучше использовать комбинацию делегирования и прямых слушателей событий для определённых элементов. В некоторых ситуациях это может упростить код и сделать его более удобным в сопровождении.

Заключение

В заключение можно сказать, что делегирование событий — это ценная стратегия для написания более эффективного и масштабируемого кода JavaScript. По мере роста сложности проектов использование делегирования событий может помочь создать чистый код и эффективные приложения. Это не только улучшит работу веб-приложений, но и упростит сопровождение и адаптацию кода. Используя делегирование событий, можно добавить слушателя к главному родительскому элементу (например, document или определённому контейнеру) и обрабатывать события от любого из его дочерних элементов. Это позволяет обрабатывать множество событий с меньшим количеством кода, делая приложение более эффективным и простым в управлении.

Комментарии


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

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

Новое в Symfony 7.2: Устаревания