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

Как отловить событие на стадии погружения?

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

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

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

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

Как отловить событие на стадии погружения (Capturing)

События в DOM проходят три стадии: погружение (capturing), цель (target), всплытие (bubbling). Чтобы отловить событие на стадии погружения, нужно использовать третий параметр addEventListener() или свойство capture в опциях.

Три стадии события

Когда пользователь кликает на элемент, событие проходит 3 фазы:

     Window
       ↓ (1. CAPTURING — погружение вниз)
   Document
       ↓
    Body
       ↓
   Div#container
       ↓
   Button (2. TARGET — достижение цели)
       ↑ (3. BUBBLING — всплытие вверх)
    Div#container
       ↑
    Body
       ↑
   Document
       ↑
     Window

Синтаксис addEventListener

// Синтаксис
element.addEventListener(event, callback, useCapture);

// useCapture = true  → слушаем на стадии CAPTURING (погружение)
// useCapture = false → слушаем на стадии BUBBLING (всплытие) — ПО УМОЛЧАНИЮ

Пример 1: Базовое использование

<div id="container">
  <button id="btn">Click me</button>
</div>
const container = document.getElementById('container');
const btn = document.getElementById('btn');

// Слушаем ВСПЛЫТИЕ (обычный режим)
container.addEventListener('click', () => {
  console.log('Container: BUBBLING');
}, false); // false — это по умолчанию

// Слушаем ПОГРУЖЕНИЕ
container.addEventListener('click', () => {
  console.log('Container: CAPTURING');
}, true); // true — активируем режим capturing

btn.addEventListener('click', () => {
  console.log('Button: TARGET');
});

// При клике на кнопку:
// Container: CAPTURING  (первым!)
// Button: TARGET
// Container: BUBBLING   (потом)

Объект с опциями (современный подход)

// Вместо булева значения можно использовать объект
container.addEventListener('click', handler, {
  capture: true,        // Погружение или всплытие
  once: true,           // Срабатывает только один раз
  passive: false        // Может ли handler вызвать preventDefault()
});

// Или с несколькими опциями
window.addEventListener('scroll', handleScroll, {
  passive: true,        // Улучшает производительность скролла
  capture: false
});

Пример 2: Практический случай использования

Задача: Отловить событие до того, как оно достигнет целевого элемента

<div id="parent" style="border: 2px solid blue; padding: 20px;">
  <input id="input" type="text" placeholder="Введи текст" />
</div>
const parent = document.getElementById('parent');
const input = document.getElementById('input');

// На стадии CAPTURING: можем остановить распространение
parent.addEventListener('focus', (e) => {
  console.log('CAPTURING: Элемент получил фокус');
  
  // Можем отменить действие ДО достижения цели
  if (e.target.value === '') {
    console.log('Пусто — останавливаем распространение');
    e.stopPropagation();
  }
}, true); // true = capturing

// На стадии TARGET
input.addEventListener('focus', (e) => {
  console.log('TARGET: Инпут получил фокус');
});

// На стадии BUBBLING
parent.addEventListener('focus', (e) => {
  console.log('BUBBLING: Фокус всплыл вверх');
}, false);

Пример 3: Делегирование с capturing

Делегирование событий работает с обеими фазами:

<ul id="list">
  <li><button class="delete">Удалить</button></li>
  <li><button class="delete">Удалить</button></li>
  <li><button class="delete">Удалить</button></li>
</ul>
const list = document.getElementById('list');

// Обычный способ (всплытие)
list.addEventListener('click', (e) => {
  if (e.target.classList.contains('delete')) {
    console.log('Удаляем элемент (BUBBLING)');
  }
}, false);

// С погружением
list.addEventListener('click', (e) => {
  if (e.target.classList.contains('delete')) {
    console.log('Удаляем элемент (CAPTURING)');
    e.stopPropagation(); // Может помешать другим обработчикам!
  }
}, true);

Пример 4: stopPropagation и stopImmediatePropagation

const container = document.getElementById('container');
const btn = document.getElementById('btn');

// CAPTURING
container.addEventListener('click', (e) => {
  console.log('1. Container CAPTURING');
  // e.stopPropagation(); // Остановит погружение вниз
}, true);

// TARGET — первый обработчик
btn.addEventListener('click', (e) => {
  console.log('2. Button - первый обработчик');
  e.stopPropagation(); // Остановит всплытие!
});

// TARGET — второй обработчик
btn.addEventListener('click', (e) => {
  console.log('3. Button - второй обработчик');
});

// BUBBLING
container.addEventListener('click', (e) => {
  console.log('4. Container BUBBLING');
}, false);

// При клике: 1 → 2 → 3 (4 не произойдёт из-за stopPropagation)

Таблица фаз события

ФазаНазваниеПараметрПример
1Capturing (погружение)trueОт Window к элементу
2Target (цель)target phaseНа самом элементе
3Bubbling (всплытие)falseОт элемента к Window

Частые ошибки

❌ Ошибка 1: Забывают про третий параметр

// Это ВСПЛЫТИЕ, не ПОГРУЖЕНИЕ!
element.addEventListener('click', handler);

// Правильно для ПОГРУЖЕНИЯ:
element.addEventListener('click', handler, true);

❌ Ошибка 2: Используют stopPropagation без необходимости

// Это может сломать делегирование!
element.addEventListener('click', (e) => {
  e.stopPropagation(); // Осторожно!
});

❌ Ошибка 3: Путают event.target и event.currentTarget

container.addEventListener('click', (e) => {
  console.log(e.target);        // Элемент, на который кликнули
  console.log(e.currentTarget);  // Container (текущий слушатель)
}, true);

Когда использовать capturing

Используй capturing когда:

  • Нужно перехватить событие ДО целевого элемента
  • Хочешь отменить действие на ранней стадии
  • Реализуешь сложное управление фокусом

Избегай capturing когда:

  • Можно решить проблему через всплытие
  • Есть риск сломать делегирование других обработчиков
  • Нужна простота и читаемость

В 95% случаев хватает всплытия. Capturing — специальный инструмент для редких сценариев!

Как отловить событие на стадии погружения? | PrepBro