Что такое event delegation в JavaScript?

MiddleJavaScript · Frontend·Обновлено 11 июля 2026
Коротко
Event delegation — это паттерн, при котором один обработчик события вешается на родительский элемент вместо множества обработчиков на каждый дочерний, используя механизм всплытия событий.

Event Delegation

Event delegation (делегирование событий) — это техника обработки событий, основанная на механизме всплытия (bubbling). Вместо того чтобы назначать обработчик каждому дочернему элементу отдельно, один обработчик размещается на общем родителе. Когда событие происходит на дочернем элементе, оно всплывает до родителя, где и перехватывается.

Как это работает

Когда пользователь кликает на <li> внутри <ul>, событие сначала срабатывает на <li>, затем всплывает до <ul>, затем до <body>, и так далее до document. Мы ловим событие на <ul> и через event.target определяем, на каком именно дочернем элементе произошло действие.

Преимущества

  • Производительность — один обработчик вместо сотен. Меньше памяти, меньше нагрузки при монтировании.
  • Динамические элементы — обработчик автоматически работает для новых элементов, добавленных в DOM после инициализации.
  • Упрощение кода — не нужно добавлять/удалять обработчики при изменении списка элементов.

Ограничения

  • Не все события всплывают: focus, blur, mouseenter, mouseleave — не всплывают (есть аналоги focusin, focusout, mouseover, mouseout).
  • Нужно аккуратно фильтровать event.target, чтобы не реагировать на нецелевые клики по родителю.
  • Если дочерний элемент содержит вложенные теги (например, <button><span>Текст</span></button>), event.target может указывать на <span>, а не на <button> — нужно использовать closest().

Пример без делегирования (плохо)

// Навешиваем обработчик на каждый элемент — дорого и не работает для новых элементов
document.querySelectorAll('.item').forEach(item => {
  item.addEventListener('click', handleClick);
});

Применение в реальных проектах

Delегирование активно используется в библиотеках (jQuery использовал .on(selector)) и фреймворках. React реализует синтетическую систему событий именно через делегирование — до React 17 все обработчики вешались на document, с React 17 — на корневой DOM-контейнер.

Что хочет услышать интервьюер

Кандидат объясняет связь делегирования с механизмом всплытия (event bubbling)

Упоминает event.target для определения источника события и использование closest() для безопасной фильтрации

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

Знает, что не все события всплывают, и называет примеры (focus/blur)

Может привести практический пример кода или упомянуть, что React использует этот паттерн

Пример: Базовое делегирование на списке

const list = document.querySelector<HTMLUListElement>('#todo-list')!;

list.addEventListener('click', (event: MouseEvent) => {
  // closest находит ближайший элемент по селектору, включая сам target
  const item = (event.target as Element).closest<HTMLLIElement>('li.todo-item');

  // Если клик был не по li.todo-item — ничего не делаем
  if (!item) return;

  const id = item.dataset.id;
  console.log('Клик по задаче с id:', id);
  item.classList.toggle('done');
});

// Новые элементы автоматически подхватывают обработчик
function addTodoItem(text: string, id: string): void {
  const li = document.createElement('li');
  li.className = 'todo-item';
  li.dataset.id = id;
  li.textContent = text;
  list.appendChild(li); // обработчик уже работает для этого элемента
}

Пример: Делегирование с несколькими типами действий

interface Action {
  type: 'edit' | 'delete' | 'complete';
  id: string;
}

function parseAction(target: Element): Action | null {
  const btn = target.closest<HTMLButtonElement>('[data-action]');
  if (!btn) return null;

  const type = btn.dataset.action as Action['type'];
  const id = btn.closest('[data-id]')?.getAttribute('data-id');

  if (!type || !id) return null;
  return { type, id };
}

document.querySelector('#app')!.addEventListener('click', (event: MouseEvent) => {
  const action = parseAction(event.target as Element);
  if (!action) return;

  // Диспатчим нужное действие по типу
  switch (action.type) {
    case 'edit':     handleEdit(action.id);     break;
    case 'delete':   handleDelete(action.id);   break;
    case 'complete': handleComplete(action.id); break;
  }
});

function handleEdit(id: string): void { console.log('Редактировать', id); }
function handleDelete(id: string): void { console.log('Удалить', id); }
function handleComplete(id: string): void { console.log('Завершить', id); }

Типичные ошибки

Путают event.target (элемент, на котором произошло событие) и event.currentTarget (элемент с обработчиком)

Не учитывают вложенные теги внутри кликабельного элемента — проверяют target напрямую вместо использования closest()

Считают, что делегирование применимо ко всем событиям, забывая про non-bubbling события (focus, blur, mouseenter)

Не понимают разницу между фазами захвата и всплытия, путаются в третьем аргументе addEventListener

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

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.5
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее