Пузырьки событий — это термин, который вы могли встретить в своих путешествиях по JavaScript. Это относится к порядку, в котором вызываются обработчики событий, когда один элемент вложен во второй элемент, и оба элемента зарегистрировали прослушиватель для одного и того же события (например, щелчок).
Но всплески событий — это только одна часть головоломки. Это часто упоминается в связи с захватом и распространением событий. А для работы с событиями в JavaScript необходимо четкое понимание всех трех концепций, например, если вы хотите реализовать шаблон делегирования событий .
В этом посте я объясню каждый из этих терминов и продемонстрирую, как они сочетаются друг с другом. Я также покажу вам, как базовое понимание потока событий JavaScript может дать вам детальный контроль над вашим приложением. Обратите внимание, что это не учебник по событиям, поэтому предполагается знакомство с темой. Если вы хотите узнать больше о событиях в целом, почему бы не проверить нашу книгу: JavaScript: от новичка до ниндзя .
Что такое распространение событий?
Начнем с распространения событий. Это общий термин как для всплытия событий, так и для захвата событий. Рассмотрим типичную разметку для создания списка связанных изображений, например, для галереи миниатюр:
<ul> <li><a href="..."><img src="..." alt=""></a> <li><a href="..."><img src="..." alt=""></a> ... <li><a href="..."><img src="..." alt=""></a> </ul>
Щелчок по изображению генерирует не только событие click
для соответствующего элемента IMG
, но и для родительского элемента A
, для дедушки LI
и т. Д., Проходя весь путь до всех предков элемента, прежде чем завершиться в объекте window
,
В терминологии DOM изображение является целью события , самым внутренним элементом, над которым произошел щелчок. Цель события и его предки от родительского объекта до window
объекта образуют ветвь в дереве DOM. Например, в галерее изображений эта ветвь будет состоять из узлов: IMG
, A
, LI
, UL
, BODY
, HTML
, document
, window
.
Обратите внимание, что
window
самом деле не является узлом DOM, но оно реализует интерфейсEventTarget
, поэтому для простоты мы обрабатываем его так, как будто это родительский узел объекта документа.
Эта ветвь важна, потому что это путь, по которому события распространяются (или проходят). Это распространение — это процесс вызова всех слушателей для данного типа события, привязанных к узлам в ветви. Каждый слушатель будет вызываться с объектом event
который собирает информацию, относящуюся к событию (подробнее об этом позже).
Помните, что несколько слушателей могут быть зарегистрированы на узле для одного и того же типа события. Когда распространение достигает одного такого узла, слушатели вызываются в порядке их регистрации.
Следует также отметить, что определение ветвления является статическим, то есть оно устанавливается при первоначальной отправке события. Изменения дерева, происходящие во время обработки события, будут игнорироваться.
Распространение является двунаправленным, от окна до цели события и обратно. Это распространение можно разделить на три этапа:
- От окна к родителю цели события: это фаза захвата
- Сама цель события: это целевая фаза
- От родителя цели события обратно к окну: пузырьковая фаза
Что отличает эти фазы, так это тип слушателей, которые называются.
Этап захвата событий
На этом этапе вызываются только прослушиватели захвата , а именно те прослушиватели, которые были зарегистрированы с использованием значения true
для третьего параметра addEventListener :
el.addEventListener('click', listener, true)
Если этот параметр пропущен, его значение по умолчанию равно false и слушатель не является перехватчиком.
Таким образом, на этом этапе вызываются только средства захвата, найденные на пути от окна к целевому родителю события.
Целевая фаза события
На этом этапе будут вызываться все слушатели, зарегистрированные на цели события, независимо от значения их флага захвата.
Фаза событий
Во время фазы всплытия событий будут вызываться только те, кто не захватывает. То есть только слушатели, зарегистрированные со значением false
для третьего параметра addEventListener()
:
el.addEventListener('click', listener, false) // listener doesn't capture el.addEventListener('click', listener) // listener doesn't capture
Обратите внимание, что пока все события направляются к цели события с фазой захвата, focus
, blur
, load
и некоторыми другими, не всплывают. То есть их путешествие останавливается после целевой фазы.
Поэтому в конце распространения каждый прослушиватель на ветви вызывается ровно один раз.
Пузырьки событий происходят не для всех видов событий. Во время распространения слушатель может узнать, .bubbles
ли событие, прочитав логическое свойство .bubbles
объекта event
.
Три этапа потока событий показаны на следующей диаграмме из спецификации W3C UIEvents .
Copyright © 2016 World Wide Web Consortium, (MIT, ERCIM, Keio, Beihang) .
Доступ к информации о распространении
Я уже упоминал свойство .bubbles
объекта event
. Этот объект предоставляет ряд других свойств, которые доступны слушателям для доступа к информации, касающейся распространения.
- e.target ссылается на цель события.
- e.currentTarget — это узел, на котором был зарегистрирован работающий прослушиватель. Это то же значение контекста вызова слушателя, то есть значение, на которое ссылается ключевое слово
this
. - Мы даже можем узнать текущую фазу с e.eventPhase . Это целое число, которое относится к одной из трех констант конструктора
Event
BUBBLING_PHASE
,BUBBLING_PHASE
иAT_TARGET
.
Применяя это на практике
Давайте рассмотрим вышеизложенные концепции на практике. На следующем рисунке пять вложенных квадратных ячеек с именем b0
… b4
. Первоначально видна только внешняя коробка b0
; внутренние отобразятся, когда на них наведет указатель мыши. Когда мы нажимаем на поле, журнал потока распространения отображается в таблице справа.
Можно даже щелкнуть за пределами блоков: в этом случае целью события будет элемент BODY
или HTML
, в зависимости от местоположения экрана щелчка.
Остановка распространения
Распространение события можно остановить в любом слушателе, вызвав метод stopPropagation объекта события. Это означает, что все прослушиватели, зарегистрированные на узлах на пути распространения, которые следуют за текущей целью, не будут вызваны. Вместо этого все остальные оставшиеся слушатели, прикрепленные к текущей цели, все равно получат событие.
Мы можем проверить это поведение с помощью простого форка предыдущей демонстрации , просто вставив вызов stopPropagation()
в один из слушателей. Здесь мы добавили нового слушателя в качестве средства захвата к списку обратных вызовов, зарегистрированных в window
:
window.addEventListener('click', e => { e.stopPropagation(); }, true); window.addEventListener('click', listener('c1'), true); window.addEventListener('click', listener('c2'), true); window.addEventListener('click', listener('b1')); window.addEventListener('click', listener('b2'));
Таким образом, при любом щелчке по полю распространение останавливается рано, достигая только слушателей захвата в window
.
Остановка немедленного распространения
Как указано в его названии, stopImmediatePropagation сразу же тормозит, не давая даже братьям и сестрам текущего слушателя получить событие. Мы можем увидеть это с минимальным изменением последней ручки :
window.addEventListener('click', e => { e.stopImmediatePropagation(); }, true); window.addEventListener('click', listener('c1'), true); window.addEventListener('click', listener('c2'), true); window.addEventListener('click', listener('b1')); window.addEventListener('click', listener('b2'));
Теперь ничего не выводится в таблицу журнала, ни строки захвата окна c1
и c2
, потому что распространение останавливается после выполнения нового слушателя.
Отмена мероприятия
Некоторые события связаны с действием по умолчанию, которое браузер выполняет в конце распространения. Например, нажатие на элемент ссылки или нажатие на кнопку отправки формы заставляет браузер перейти на новую страницу или отправить форму соответственно.
Можно избежать выполнения таких действий по умолчанию с отменой события, вызвав еще один метод объекта события, e.preventDefault , в слушателе.
Вывод
В связи с этим я надеюсь показать вам, как происходит всплытие и захват событий в JavaScript. Если у вас есть какие-либо вопросы или комментарии, я был бы рад услышать их в обсуждении ниже.
Ссылки
- Объектная модель документа (DOM) Уровень 2 Спецификация событий
- W3C DOM4 — События
- ДОМ — Уровень жизни — События
- События пользовательского интерфейса W3C — архитектура событий DOM
- MSN — События и DOM
Эта статья была рецензирована Яфи Берхану и Домиником Майерсом . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!