Skip to main content

Делегування подій (Event Delegation)

Делегування подій це прийом у JavaScript, коли ми вішаємо обробник не на кожен елемент, а на їхнього спільного батька, і використовуємо спливання подій, щоб визначити, на якому саме елементі сталася дія. Іншими словами: один слухач керує подіями багатьох дочірніх елементів.

Теорія

TL;DR

  • Один обробник на батьківському елементі замість сотні обробників на дітях.
  • Працює завдяки спливанню: подія від дитини піднімається до батька, де її й ловлять.
  • Конкретний елемент визначається через event.target, зазвичай за допомогою matches() або closest().
  • Автоматично охоплює елементи, додані в DOM пізніше.
  • Не працює для подій, які не спливають (focus, blur, mouseenter, mouseleave).

Швидкий приклад

Треба обробити кліки по 100 кнопках.

Наївно, слухач на кожній кнопці:

javascript
document.querySelectorAll('button').forEach(btn => { btn.addEventListener('click', () => { console.log('clicked'); }); });

Правильно, делегування:

javascript
document.body.addEventListener('click', (event) => { if (event.target.matches('button')) { console.log('clicked'); } });

Навіщо це потрібно

Ключові тези, які варто назвати на співбесіді:

  • Менше обробників, отже менше пам'яті і швидша робота сторінки.
  • Працює для динамічно доданих елементів: навіть якщо елемент з'явився пізніше, батьківський обробник його все одно «спіймає».
  • Краща керованість: уся логіка в одному місці, а не розмазана по дереву DOM.
  • Простіше прибирання: щоб від'єднати обробку, достатньо зняти один слухач, а не обходити всі вузли.

Як це працює

  1. Користувач клікає по кнопці.
  2. Подія спливає вгору по дереву DOM.
  3. Обробник на body (або на будь-якому спільному предку) перехоплює подію.
  4. Ми перевіряємо event.target, щоб визначити елемент-кандидат.

Надійніший варіант перевірки, коли всередині кнопки є вкладена розмітка (іконка, <span>):

javascript
list.addEventListener('click', (event) => { const item = event.target.closest('li'); if (!item || !list.contains(item)) return; console.log('Клік по пункту:', item.dataset.id); });

closest() піднімається від фактичної цілі вгору, поки не знайде відповідний елемент, тож клік по іконці всередині пункту теж спрацює правильно.

target проти currentTarget

Це найчастіша помилка на співбесіді:

  • event.target це елемент, по якому клікнули, тобто джерело події.
  • event.currentTarget це елемент, на якому висить слухач, тобто батько.
ВластивістьНа що вказуєУ делегуванні
event.targetРеальна ціль подіїДочірній елемент, який нас цікавить
event.currentTargetЕлемент зі слухачемБатьківський контейнер

Усередині звичайної функції-обробника this дорівнює event.currentTarget, а не target.

Коли делегування не підходить

  • Подія не спливає. Для focus і blur беріть focusin і focusout, для mouseenter і mouseleave беріть mouseover і mouseout.
  • Хтось на шляху викликав stopPropagation(). Тоді подія просто не дійде до батьківського слухача, і делегування мовчки перестане працювати.
  • Дуже гарячі події на великому контейнері. Для mousemove чи scroll один обробник на document може стати вузьким місцем, там краще точкові слухачі або throttling.

Коротке визначення, якщо інтерв'юер просить стисло: делегування подій це техніка, коли один обробник на батьківському елементі обробляє події багатьох дочірніх елементів, використовуючи спливання подій.

Типові помилки

  • Плутати target і currentTarget, а потім дивуватися, чому перевірка завжди дає батьківський контейнер.
  • Перевіряти лише event.target.matches(...) там, де ціль може бути вкладеним вузлом. Використовуйте closest().
  • Вішати слухач одразу на document без потреби. Беріть найближчого спільного предка: менше зайвих спрацювань і менше конфліктів з іншим кодом.
  • Забувати перевірити, що знайдений елемент справді всередині контейнера. Без container.contains(item) можна зловити збіг із чужого блока.
  • Розраховувати на делегування для подій, які не спливають, і потім довго шукати, чому обробник не викликається.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.