Делегирование событий: эффективная обработка множества элементов

Почему делегирование — это мощный инструмент? ️

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

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

// Плохо: 100 обработчиков для 100 кнопок
document.querySelectorAll('.btn').forEach(btn => {
  btn.addEventListener('click', () => {
    console.log('Клик!');
  });
});

// Хорошо: 1 обработчик на родителя
document.querySelector('.btn-container').addEventListener('click', (event) => {
  if (event.target.classList.contains('btn')) {
    console.log('Клик по кнопке!');
  }
});

🔥 Секрет эффективности: Вместо 100 слушателей — один. Меньше нагрузки, проще масштабировать!


Как работает всплытие событий? 🌊

Когда происходит клик (или другое событие), оно проходит три фазы:
1. Capture — сверху вниз до целевого элемента.
2. Target — достижение элемента.
3. Bubbling — снизу вверх обратно к корню.

Делегирование использует bubbling. Даже если элемент добавлен динамически, событие «дойдет» до родителя.

document.getElementById('list').addEventListener('click', (event) => {
  // Проверяем, был ли клик по элементу списка
  if (event.target.tagName === 'LI') {
    console.log('Клик по li:', event.target.textContent);
  }
});

💡 Проверяйте event.target — это исходный элемент, на котором произошло событие.


Практика: делегирование в реальных проектах ️

Пример 1: Динамически добавляемые элементы

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

// HTML: <ul id="tasks"></ul>
const taskList = document.getElementById('tasks');

// Добавляем задачи динамически
function addTask(text) {
  const li = document.createElement('li');
  li.textContent = text;
  taskList.appendChild(li);
}

// Вешаем ОДИН обработчик на список
taskList.addEventListener('click', (event) => {
  if (event.target.tagName === 'LI') {
    event.target.classList.toggle('completed');
  }
});

// Добавляем задачи
addTask('Изучить делегирование');
addTask('Сделать проект');

Пример 2: Универсальный обработчик для таблицы

Таблица с кнопками удаления строк. Вместо обработчика на каждую кнопку — один на всю таблицу.

document.querySelector('table').addEventListener('click', (event) => {
  if (event.target.classList.contains('delete-btn')) {
    const row = event.target.closest('tr');
    row.remove();
  }
});

🚀 closest() — ваш друг — ищет ближайший родительский элемент, подходящий под селектор.


Когда делегирование не подходит? ⚠️

  1. События без всплытия — например, focus, blur.
  2. Высокая вложенность — если элементов много, проверка event.target может стать сложной.
  3. Необходимость прервать всплытие — если где-то есть stopPropagation(), делегирование может сломаться.

Итог: 3 причины использовать делегирование

  1. Экономия памяти — один обработчик вместо сотен.
  2. Работа с динамическими элементами — не нужно перевешивать слушатели.
  3. Чистый код — логика обработки событий централизована.
// Финал: универсальный шаблон для делегирования
parentElement.addEventListener('event', (e) => {
  if (e.target.matches('селектор')) {
    // Ваш код здесь...
  }
});

Теперь — в бой! Попробуйте переписать свой проект с делегированием и почувствуйте разницу. 😎

Скрыть рекламу навсегда

🌱 Индвидидулаьные занятия

Индивидуальные онлайн-занятия по программированию для детей и подростков

Личный подход, без воды, с фокусом на понимание и реальные проекты.

🚀 Записаться на занятие