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

Как поймать событие на стадии Capturing?

1.7 Middle🔥 112 комментариев
#JavaScript Core

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

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

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

Ловля событий на стадии Capturing

Это важный вопрос о механизме распространения событий (Event Bubbling и Capturing). Разберу в деталях.

Как работает распространение событий

В браузере событие проходит три фазы:

1. CAPTURING - от корня документа к целевому элементу
2. TARGET - событие достигает целевого элемента
3. BUBBLING - от целевого элемента вверх к корню

Полный путь: window -> document -> html -> body -> ... -> target -> ... -> body -> html -> document -> window

Использование третьего параметра addEventListener

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

// useCapture = true  -> ловим на фазе CAPTURING
// useCapture = false -> ловим на фазе BUBBLING (по умолчанию)

Пример: ловим на фазе Capturing

// HTML
// <div class="parent">
//   <div class="child">
//     <button>Нажми</button>
//   </div>
// </div>

const button = document.querySelector('button');
const parent = document.querySelector('.parent');

// Слушатель на CAPTURING фазе
parent.addEventListener('click', (e) => {
  console.log('CAPTURING: родитель получит событие ПЕРВЫМ');
}, true);  // true = используем capturing

// Слушатель на BUBBLING фазе (стандарт)
button.addEventListener('click', (e) => {
  console.log('TARGET: событие достигло кнопки');
}, false); // false = bubbling (по умолчанию)

// Слушатель на BUBBLING фазе
parent.addEventListener('click', (e) => {
  console.log('BUBBLING: родитель получит событие ПОСЛЕДНИМ');
}, false);

Порядок логирования при клике на button:

1. CAPTURING: родитель получит событие ПЕРВЫМ
2. TARGET: событие достигло кнопки
3. BUBBLING: родитель получит событие ПОСЛЕДНИМ

Более сложный пример

const grandparent = document.querySelector('.grandparent');
const parent = document.querySelector('.parent');
const child = document.querySelector('.child');

// Capturing слушатели (true)
grandparent.addEventListener('click', () => {
  console.log('Grandparent CAPTURING');
}, true);

parent.addEventListener('click', () => {
  console.log('Parent CAPTURING');
}, true);

// Target слушатели (нет разницы false/true для целевого элемента)
child.addEventListener('click', () => {
  console.log('Child TARGET');
}, true);

child.addEventListener('click', () => {
  console.log('Child TARGET (second)');
}, false);

// Bubbling слушатели (false)
parent.addEventListener('click', () => {
  console.log('Parent BUBBLING');
}, false);

grandparent.addEventListener('click', () => {
  console.log('Grandparent BUBBLING');
}, false);

Вывод при клике на child:

Grandparent CAPTURING
Parent CAPTURING
Child TARGET
Child TARGET (second)
Parent BUBBLING
Grandparent BUBBLING

Управление распространением

const element = document.querySelector('button');

element.addEventListener('click', (event) => {
  // Остановить capturing и bubbling полностью
  event.stopPropagation();
  
  // Остановить только bubbling (capturing продолжится)
  // event.stopImmediatePropagation();
  
  console.log('Событие не распространится дальше');
}, true);  // capturing phase

React пример

В React ситуация сложнее из-за synthetic events:

export function EventCapturingExample() {
  const handleParentCapturing = (e) => {
    // В React нет встроенной поддержки capturing
    // Нужно использовать нативный API через useEffect
    console.log('Parent bubbling (React стандартно)');
  };

  // Правильный способ в React - использовать нативный addEventListener
  const parentRef = useRef(null);
  const childRef = useRef(null);

  useEffect(() => {
    const parent = parentRef.current;
    const child = childRef.current;

    // Capturing фаза
    const captureHandler = (e) => {
      console.log('CAPTURING:', e.target);
    };

    parent?.addEventListener('click', captureHandler, true);
    child?.addEventListener('click', captureHandler, true);

    return () => {
      parent?.removeEventListener('click', captureHandler, true);
      child?.removeEventListener('click', captureHandler, true);
    };
  }, []);

  return (
    <div ref={parentRef} className="border p-4">
      <button ref={childRef} onClick={handleParentCapturing}>
        Click me
      </button>
    </div>
  );
}

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

Capturing полезен для:

  1. Event delegation с приоритетом - перехватить событие до того как его перехватит вложенный элемент
const form = document.querySelector('form');
form.addEventListener('submit', (e) => {
  // Это сработает раньше, чем handler у input
  console.log('Валидация на уровне формы');
  e.preventDefault();
}, true);
  1. Остановка распространения события до того как оно достигнет целевого элемента
document.addEventListener('click', (e) => {
  if (e.target.matches('.ignore-clicks')) {
    e.stopPropagation();
    return false;
  }
}, true);
  1. Глобальная обработка ошибок в фазе capturing

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

// ❌ Забыли третий параметр
element.addEventListener('click', handler);  // Это будет BUBBLING

// ✅ Правильно для capturing
element.addEventListener('click', handler, true);

// ❌ Используешь старый способ
element.onclick = handler;  // Только bubbling, старо

// ✅ Правильно
element.addEventListener('click', handler, true);

Производительность

Использование capturing может быть полезно для оптимизации:

// Вместо добавления listener каждому элементу
const items = document.querySelectorAll('.item');
items.forEach(item => {
  item.addEventListener('click', handler); // Много слушателей
});

// Лучше использовать capturing на родителе
const container = document.querySelector('.container');
container.addEventListener('click', (e) => {
  if (e.target.matches('.item')) {
    handler(e);
  }
}, true); // Один слушатель на capturing фазе

Итоговая таблица

ПараметрФазаПорядокКогда использовать
trueCapturingРаньшеПерехват до целевого элемента
falseBubblingПозжеСтандартный подход (по умолчанию)

В большинстве случаев используешь Bubbling (false или ничего не указываешь). Capturing нужен редко, но важно его понимать для решения сложных проблем с обработкой событий.

Как поймать событие на стадии Capturing? | PrepBro