← Назад к вопросам

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

1.0 Junior🔥 201 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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

Делегирование событий (Event Delegation) - это техника, при которой вместо того, чтобы вешать обработчик события на каждый элемент, вы вешаете один обработчик на родителя и используете механизм всплытия события (event bubbling) для определения целевого элемента.

Как события всплывают (Bubbling)

Когда происходит событие, оно проходит две фазы:

1. Capture фаза (спуск)
window -> document -> html -> body -> div -> button

2. Target фаза
Клик произошел на кнопке

3. Bubbling фаза (подъем)
button -> div -> body -> html -> document -> window

Это очень важно для делегирования:

// Событие click на <button> всплывает до родителя
<div id="parent">
  <button id="button">Click me</button>
</div>

const parent = document.getElementById('parent');
parent.addEventListener('click', (event) => {
  console.log('Событие поймано на родителе');
  console.log(event.target); // <button>
});

Проблема без делегирования

<ul id="list">
  <li><a href="#">Item 1</a></li>
  <li><a href="#">Item 2</a></li>
  <li><a href="#">Item 3</a></li>
  <li><a href="#">Item 100</a></li>
</ul>

Без делегирования нужно вешать обработчик на КАЖДУЮ ссылку:

// Плохо: 100 обработчиков!
const links = document.querySelectorAll('a');
links.forEach(link => {
  link.addEventListener('click', handleClick);
});

function handleClick(event) {
  event.preventDefault();
  console.log('Clicked:', event.target.textContent);
}

Проблемы:

  1. Много кода
  2. Много памяти (100 обработчиков)
  3. Если добавить новую ссылку динамически - обработчик не будет на ней

Решение: Делегирование событий

// Хорошо: 1 обработчик на родителе
const list = document.getElementById('list');
list.addEventListener('click', (event) => {
  if (event.target.tagName === 'A') {
    event.preventDefault();
    console.log('Clicked:', event.target.textContent);
  }
});

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

  1. Один обработчик вместо 100
  2. Экономим память
  3. Динамически добавленные элементы автоматически ловятся
  4. Код чище и понятнее

Практический пример: Корзина товаров

function ShoppingCart() {
  const [items, setItems] = useState([
    { id: 1, name: 'Item 1', quantity: 1 },
    { id: 2, name: 'Item 2', quantity: 2 },
    { id: 3, name: 'Item 3', quantity: 1 },
  ]);

  const handleCartClick = (event: React.MouseEvent) => {
    const button = (event.target as HTMLElement).closest('button');
    if (!button) return;

    const itemId = parseInt(button.dataset.itemId!);
    const action = button.dataset.action;

    if (action === 'increase') {
      setItems(items.map(item =>
        item.id === itemId
          ? { ...item, quantity: item.quantity + 1 }
          : item
      ));
    } else if (action === 'decrease') {
      setItems(items.map(item =>
        item.id === itemId
          ? { ...item, quantity: Math.max(0, item.quantity - 1) }
          : item
      ));
    } else if (action === 'remove') {
      setItems(items.filter(item => item.id !== itemId));
    }
  };

  return (
    <div onClick={handleCartClick}>
      {items.map(item => (
        <div key={item.id} className="cart-item">
          <span>{item.name}</span>
          <span>Qty: {item.quantity}</span>
          <button data-item-id={item.id} data-action="decrease">
            -
          </button>
          <button data-item-id={item.id} data-action="increase">
            +
          </button>
          <button data-item-id={item.id} data-action="remove">
            Remove
          </button>
        </div>
      ))}
    </div>
  );
}

Вместо:

// Плохо: обработчик на каждой кнопке
{items.map(item => (
  <div key={item.id}>
    <button onClick={() => handleIncrease(item.id)}>+</button>
    <button onClick={() => handleDecrease(item.id)}>-</button>
    <button onClick={() => handleRemove(item.id)}>Remove</button>
  </div>
))}

event.target vs event.currentTarget

Это критично для делегирования:

const parent = document.getElementById('parent');
parent.addEventListener('click', (event) => {
  console.log(event.target);       // На каком элементе случилось событие
  console.log(event.currentTarget); // На каком элементе обработчик
});

// <div id="parent">
//   <button>Click</button>
// </div>

// Если кликнуть на button:
// event.target = <button>
// event.currentTarget = <div id="parent">

closest() - удобный метод

Для сложных структур используй closest():

<ul class="list">
  <li class="item">
    <span class="text">Item 1</span>
    <button class="delete">Delete</button>
  </li>
</ul>

const list = document.querySelector('.list');
list.addEventListener('click', (event) => {
  const deleteBtn = (event.target as Element).closest('.delete');
  if (deleteBtn) {
    const item = deleteBtn.closest('.item');
    console.log('Удаление:', item);
  }
});

Когда НЕ использовать делегирование

События которые не всплывают:

  • focus, blur
  • load, unload
  • scroll, resize
  • mouseenter, mouseleave (используй mouseover/mouseout)
// Не сработает делегирование!
const input = document.getElementById('input');
input.addEventListener('focus', () => {
  // Это нужно вешать на сам input
});

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

В React делегирование встроено

React использует синтетические события с автоматическим делегированием:

// React автоматически использует делегирование
<ul>
  {items.map(item => (
    <li key={item.id}>
      <button onClick={(e) => handleClick(item.id, e)}>
        {item.name}
      </button>
    </li>
  ))}
</ul>

// React вешает обработчик на корень и использует
// делегирование для всех click событий

Заключение

Делегирование событий - это:

  1. Эффективно - один обработчик вместо множества
  2. Экономит память - меньше обработчиков
  3. Работает с динамическими элементами - новые элементы автоматически ловятся
  4. Код чище - логика в одном месте
  5. Встроено в React - не нужно думать об этом

Это основной паттерн в modern веб-разработке.