Как предотвратить всплытие события при клике на вложенные контейнеры в JavaScript
Что проверяют
Знаете ли вы, как событие проходит по DOM, и понимаете ли, что stopPropagation() это крайняя мера, а не первый инструмент. Следом обычно спрашивают разницу с preventDefault() и как решить задачу без остановки всплытия.
Как отвечать
Сначала механика. Событие идёт в три фазы: погружение от window к цели, фаза цели, всплытие обратно вверх. Обработчики из addEventListener по умолчанию срабатывают на всплытии, на погружении только с { capture: true }.
Инструменты:
event.stopPropagation(): событие не пойдёт к следующим элементам, но остальные обработчики на текущем элементе всё равно выполнятся;event.stopImmediatePropagation(): вдобавок отменяет оставшиеся обработчики на этом же элементе;- проверка цели в родителе: с
if (event.target !== event.currentTarget) return;родитель реагирует только на клик по себе, и никому ничего не ломается; - делегирование: один обработчик на контейнере и
event.target.closest('[data-action]'), чтобы понять, по чему кликнули.
Отдельно скажите, чего stopPropagation() не делает: переход по ссылке и отправку формы он не отменяет, для этого нужен preventDefault(). Это независимые вещи, их часто путают.
Если стек на React, добавьте две детали. Обработчики React с 17-й версии вешаются на корневой контейнер, и e.stopPropagation() в них останавливает всплытие по дереву компонентов. События из портала всплывают по дереву React, а не по DOM: клик внутри модалки из createPortal дойдёт до обработчика родительского компонента, хотя в DOM модалка лежит в другом месте.
Где спотыкаются
Ставят stopPropagation() везде, где мешает всплытие. Потом перестают закрываться выпадающие меню, которые слушают клик на document, и ломается аналитика кликов.
Пишут return false в обработчике addEventListener и ждут остановки всплытия. Так ведёт себя только jQuery, нативный API возвращаемое значение игнорирует.
Забывают, что всплывают не все события: focus, blur и mouseenter не всплывают, для делегирования берут focusin, focusout и mouseover.