Понимание всплытия, захвата и цели

Распространение — еще один из тех терминов разработки программного обеспечения, который звучит намного сложнее, чем есть на самом деле. Однако это очень важная концепция для понимания, и она может помочь вам отладить неожиданные проблемы в вашем коде. Распространение также имеет тенденцию появляться как обычная тема для собеседований на должности разработчиков, поэтому очень важно иметь четкое представление об этой концепции. В этом блоге я расскажу о том, что такое распространение, какие фазы может иметь событие, а также о некоторых способах распознавания распространения и о том, как оно работает.

Распространение события можно определить как

механизм, определяющий, как события распространяются или перемещаются по дереву DOM, чтобы достичь своей цели, и что с ней происходит после этого.¹

Другими словами, распространение — это поток события в том виде, в котором оно происходит в DOM, включая начало, цель и конец. К концу этой статьи это станет намного яснее. Мы сразу перейдем к примеру, чтобы лучше понять ход событий.

Див внутри Дива

Мы начнем с создания вложенного набора div на html-странице. Мы назовем их внешними, средними и внутренними и дадим одинаковые идентификаторы, чтобы мы могли отслеживать каждый из них. Наш html должен выглядеть так:

Мы также добавим несколько простых стилей и цветов CSS, чтобы было понятно, где начинается один div и заканчивается другой. Мы уже импортируем файл CSS в строку 6 нашего html, поэтому мы можем создать файл, назвать его styles.css и заполнить его следующим кодом:

Если мы откроем index.html в браузере, то увидим следующее:

Визуализация распространения

Теперь, чтобы настроить пример распространения, который мы действительно можем видеть, давайте создадим файл JavaScript (мы уже написали для него импорт в нашем html) и сошлемся на наши элементы. Мы можем создать переменную для каждого div с помощью document.querySelector(), а затем добавить прослушиватель событий для события click для каждого div.

В этом примере я хочу, чтобы каждый элемент div печатал разные операторы при нажатии на него. Теперь посмотрите, что произойдет, когда я щелкну самый внутренний блок div…

По какой-то причине мы не просто записываем в консоль «Hello my baby», а все три строки. Причина этого, конечно же, в распространении! Целевое событие — это когда мы нажимаем на внутренний div, но поскольку он вложен в два родительских div, их события также запускаются.

Три фазы события

События имеют три фазы, которые могут быть захвачены: фаза захвата, целевая фаза и фаза всплытия. Фаза захвата начинается с самого внешнего элемента и движется вниз или внутрь к элементу, который вызывает событие. Целевая фаза происходит на элементе, на котором выполняется событие, в данном случае событие «щелчок» на центральном элементе div. И, наконец, фаза всплытия относится к «пузырьку» события или каждого родительского элемента по порядку, на который влияет событие.

Эта полезная диаграмма точно описывает, как происходят фазы события, в порядке захвата, таргетинга и всплытия.

Эвент начинается с объекта Window, движется вниз до целевого объекта, а затем всплывает обратно к объекту окна.

Просмотр фаз события

Мы можем использовать встроенный в JavaScript метод eventPhase, чтобы увидеть, в какой фазе находится событие на каждом этапе распространения. Помещая наши элементы div в массив, сопоставляя этот массив и добавляя прослушиватель событий к каждому элементу div, мы можем распечатать, в какой фазе находится событие.

Обратите внимание на оператор if, else if в строках с 16 по 22. Числа 1, 2 и 3 соответствуют соответственно событию Capture, событию Target и событию Bubbling. Если бы вам нужно было зарегистрировать eventPhase несобытия, вы бы получили число 0 для отсутствия события, происходящего в данный момент.

Если мы нажмем на самый внутренний div, мы увидим следующее в консоли браузера.

Обратите внимание, что событие начинается с самого внешнего div и перемещается вниз, на этапе захвата, затем целевая фаза печатается дважды, а фаза всплытия «пузырится» обратно к внешнему div.

Примечание о добавлении прослушивателя событий

В этом примере кода мы используем .addEventListener с тремя аргументами. Первый — это тип события, в данном случае «щелчок» для щелчка мышью по элементу div, следующий аргумент — это объект, который получает уведомление, в данном случае определенная нами функция с именем showEventPhase(). Третий аргумент является необязательным, но в данном случае он называется «захват» и является логическим значением. Логическое значение захвата отвечает за

указывает, что события этого типа будут отправлены зарегистрированному listener перед отправкой любому EventTarget под ним в дереве DOM.²

Значение по умолчанию равно false, что означает, что будет отображаться фаза всплытия, начинающаяся с целевого события, а затем все фазы, происходящие над ним. Если установлено значение true, будут отправлены фазы, начинающиеся с самого внешнего элемента и двигающиеся внутрь, фаза захвата. Это полезно понять, и вы можете думать об этом так же, как о предыдущей диаграмме, где аргумент захвата указывает фазу захвата события, а значение false указывает на фазу всплытия.

Как остановить распространение

Если вы хотите остановить или изменить это поведение, JavaScript предоставляет встроенный метод для остановки распространения. Вызывая stopPropogation() для события, мы можем изменить нашу программу, чтобы она печатала только оператор, связанный с самым внутренним блоком div. Изменив наш код JavaScript на следующее:

Если мы не нажмем на самый внутренний div и не проверим консоль, мы увидим, что теперь было записано только «Hello my baby», и ни один из журналов консоли родительского div не был выполнен!

Приложения

Понимание событий распространения, всплытия и захвата, вероятно, пригодится много раз в вашем путешествии по программированию. Как было продемонстрировано выше, одним из полезных способов понимания распространения и того, как его остановить, является ситуация, когда у вас есть события, вложенные в другие элементы, которые также имеют события. Мы также можем представить ситуацию, когда лучше и/или проще разместить кнопку внутри элемента div, в котором уже есть прослушиватель событий. Вместо того, чтобы тратить слишком много времени на CSS, чтобы изменить положение кнопки и переместить ее в нашем html, мы можем просто остановить распространение в наших кнопках, даже прослушиватель, чтобы сохранить только то поведение, которое мы хотим, чтобы происходило, когда мы нажимаем кнопку.

Знание распространения также поможет вам отлаживать непредвиденные ошибки в вашем коде. Понимание того, что элемент в фазе Capturing происходит до целевого события, может помочь вам выбрать, где именно ваш код нужно изменить. То же самое касается понимания того, что всплытие происходит после целевой фазы, когда вам, возможно, придется остановить или изменить какое-то поведение, вызванное событием.

Как всегда, я настоятельно рекомендую ознакомиться с кодом в этом блоге и проверить мой репозиторий GitHub, ссылка на который приведена внизу. Кроме того, попробуйте создать свой собственный пример для отслеживания фаз событий и попробуйте остановить распространение в разных точках! Возможно, вы хотите, чтобы внутренний и средний div выполняли функцию, но не внешний. Пожалуйста, оставьте отзыв, если вы нашли это полезным или если есть что-то, что вы хотели бы прояснить, удачного кодирования!

Ресурсы:

1.



2



3.



4.