Можно ли отлавливать события на фазе погружения (capture phase) в JavaScript и как это сделать
Что проверяют
Знаете ли вы третий аргумент подписки и понимаете ли, зачем вообще нужна эта фаза.
Как отвечать
Да. Обработчик вешается на фазу погружения третьим аргументом:
el.addEventListener("click", handler, true);
el.addEventListener("click", handler, { capture: true });Напомните порядок фаз: сначала погружение от корня документа вниз к целевому элементу, затем цель, затем всплытие обратно вверх. По умолчанию обработчик срабатывает на всплытии, поэтому фазу погружения в обычном коде не замечают.
Зачем она нужна на практике:
- перехватить событие раньше всех. Обработчик на погружении сработает до обработчиков на дочерних элементах, поэтому им можно заблокировать взаимодействие: например, отключить клики поверх интерфейса во время выполнения операции;
- глобальные вещи вроде аналитики и закрытия меню, которые должны сработать независимо от того, остановит ли кто-то распространение ниже. Обработчик на всплытии в такой ситуации просто не выполнится;
- события, которые не всплывают: фокус, потеря фокуса и часть событий загрузки. Поймать их на родителе можно только на погружении, либо использовать всплывающие аналоги.
Две детали, о которых спрашивают следом:
- снятие обработчика требует того же флага. Подписались с погружением, снимайте тоже с ним, иначе обработчик останется висеть: подписки на разных фазах считаются разными;
- остановка распространения на погружении означает, что событие вообще не дойдёт до цели. Это мощно и потому опасно: чужие обработчики перестанут получать события, а причину такого поведения ищут долго.
Полезно упомянуть, что в React события синтетические и обрабатываются на корне приложения, но подписаться на фазу погружения там тоже можно через отдельные свойства с соответствующим суффиксом.
Где спотыкаются
Не знают про третий аргумент вовсе.
Не могут снять обработчик, забыв указать флаг.
Останавливают распространение на погружении и ломают весь интерфейс.
Пытаются поймать потерю фокуса на родителе через всплытие.