> Как работает всплытие и захват событий в JavaScript и какой обработчик сработает первым на div и кнопке (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Exness

Стек: JavaScript

> Пример ответа

Короткий ответ

Всплытие (bubbling) и захват (capturing) - две фазы распространения события в DOM. Сначала событие проходит от корня документа до целевого элемента (фаза захвата), затем достигает цели (фаза цели), после чего поднимается обратно к корню (фаза всплытия). Обработчик на кнопке (целевой элемент) сработает первым, так как он находится на фазе цели, которая наступает до всплытия. Обработчик на div сработает после, если он установлен на всплытие (по умолчанию).

Подробное объяснение

События в DOM распространяются в три фазы:

  1. Фаза захвата (capturing phase): событие движется от window через предков к целевому элементу. Обработчики, установленные с третьим аргументом true или { capture: true }, срабатывают на этой фазе.
  2. Фаза цели (target phase): событие достигает элемента, на котором произошло. Обработчики, привязанные к этому элементу (независимо от флага capture), срабатывают здесь.
  3. Фаза всплытия (bubbling phase): событие поднимается от целевого элемента обратно к window. Обработчики без флага capture (или с false) срабатывают на этой фазе.

Порядок срабатывания для div (родитель) и кнопки (цель):

  • Если оба обработчика установлены на всплытие (по умолчанию), то первым сработает обработчик на кнопке (фаза цели), затем на div (фаза всплытия).
  • Если обработчик на div установлен на захват (capture: true), он сработает до обработчика на кнопке, так как фаза захвата предшествует фазе цели.
  • Если оба на захват, то сначала div (захват), потом кнопка (цель), и всплытие не задействуется.

Метод stopPropagation() прерывает дальнейшее распространение события на текущей фазе. stopImmediatePropagation() также предотвращает срабатывание других обработчиков на том же элементе.

На практике

На практике всплытие используется для делегирования событий: один обработчик на родителе обрабатывает события от множества дочерних элементов. Это эффективно для динамических списков или таблиц. Захват применяется реже, например, для перехвата событий до того, как они достигнут цели, или для реализации middleware в кастомных системах.

Важно помнить, что не все события всплывают (например, focus, blur, load, unload, mouseenter, mouseleave). Для таких событий делегирование не работает напрямую, но можно использовать фазу захвата или оборачивать в всплывающие события (например, focusin/focusout).

Пример кода

HTML
<div id="parent">
<button id="child">Click me</button>
</div>
<script>
const parent = document.getElementById('parent');
const child = document.getElementById('child');
// Обработчик на всплытие (по умолчанию)
parent.addEventListener('click', () => console.log('Parent (bubbling)'));
child.addEventListener('click', () => console.log('Child (target)'));
// Обработчик на захват
parent.addEventListener('click', () => console.log('Parent (capturing)'), true);
// При клике на кнопку порядок:
// 1. Parent (capturing) - фаза захвата
// 2. Child (target) - фаза цели
// 3. Parent (bubbling) - фаза всплытия
</script>

Как отвечать на собеседовании

Начни с краткого определения фаз, затем уточни порядок для конкретного случая с div и кнопкой. Подчеркни, что по умолчанию обработчики работают на всплытие, поэтому кнопка срабатывает первой. Если интервьюер спрашивает про захват, добавь про третий аргумент. Упомяни stopPropagation и stopImmediatePropagation как способы управления. Будь готов привести пример делегирования.

Что проверяет интервьюер

Интервьюер проверяет понимание модели распространения событий в DOM, различие между фазами, умение применять это на практике (делегирование, перехват). Для senior-уровня важно также знание нюансов: какие события не всплывают, как работает event.target vs event.currentTarget, влияние stopPropagation на разные фазы.

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

  • Путаница между фазами: думают, что всплытие происходит до захвата.
  • Утверждение, что обработчик на родителе всегда срабатывает первым, если он на захвате - это верно, но забывают про фазу цели.
  • Игнорирование того, что stopPropagation работает только на текущей фазе (например, вызов в фазе захвата не остановит всплытие, если событие уже прошло захват).
  • Незнание, что addEventListener с { once: true } или { passive: true } не влияет на порядок фаз.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью