События JavaScript: всплытие, захват и распространение
События в JavaScript - это события, которые могут запускать определенную функциональность и приводить к определенному поведению. Распространенным примером события является «click» или «hover». Вы можете настроить прослушивателей для отслеживания этих событий, которые вызовут желаемую функциональность.
Что такое «распространение»?
Распространение (propagation) относится к тому, как события распространяются по дереву объектной модели документа (DOM). Дерево DOM - это структура, которая содержит родительские/дочерние/родственные элементы по отношению друг к другу. Вы можете думать о распространении как о электричестве, проходящем по проводу, пока оно не достигнет места назначения. Событие должно проходить через каждый узел в DOM, пока не достигнет конца, или если оно будет принудительно остановлено.
Всплытие и захват событий
Всплытие (bubbling) и захват (capturing) - это две фазы распространения. В их простейших определениях при всплытии поток перемещается от цели к корню, а захват перемещается от корня к цели. Однако это не имеет особого смысла без предварительного определения того, что такое цель и корень.
Целевой объект - это узел DOM, на который вы нажимаете или запускаете любое другое событие.
Например, кнопка с событием щелчка будет целью события.
Корень - это родительский элемент самого высокого уровня целевого объекта. Обычно это документ, который является родительским элементом , который является (возможно, удаленным) родительским элементом вашего целевого элемента.
Захват используется не так часто, как всплытие, поэтому наши примеры будут вращаться вокруг фазы всплытия. Однако в качестве опции EventTarget.addEventListener()
имеет необязательный третий параметр, который принимает аргумент в виде логического значения, который управляет фазой распространения. Параметр называется useCapture, и передача значения true приведет к тому, что прослушиватель будет находиться на этапе захвата. Значение по умолчанию — false, что применит его к фазе всплытия.
Как только вы инициируете событие, will _propagate _ up корня и вызовет каждый отдельный обработчик событий, связанный с одним и тем же типом. Например, если у вашей кнопки есть событие щелчка, во время фазы всплытия оно будет подниматься до корня и запускать каждое событие щелчка по пути.
Такое поведение может показаться нежелательным - и часто это не так, - но есть простой обходной путь.
Event.stopPropagation()
Эти два метода используются для решения ранее упомянутой проблемы, касающейся распространения событий. Вызов Event.stopPropagation()
предотвратит дальнейшее распространение по дереву DOM и запустит только обработчик события, из которого оно было вызвано.
<!--Try it-->
function first() {
console.log(1);
}
function second() {
console.log(2);
}
let button = document.getElementById("button");
let container = document.getElementById("container");
button.addEventListener("click", first);
container.addEventListener("click", second);
В этом примере нажатие кнопки приведет к тому, что консоль напечатает 1, 2. Если бы мы хотели изменить это так, чтобы срабатывало только событие нажатия кнопки, мы могли бы использовать Event.stopPropagation()
, чтобы немедленно остановить передачу события его родительскому элементу.
function first(event) {
event.stopPropagation();
console.log(1);
}
Эта модификация позволит консоли печатать 1, но это сразу же завершит цепочку событий, не позволяя ей достичь 2.
Отличие Event.stopImmediatePropagation()
<!--Try it-->
function first(event) {
console.log(1);
}
function second() {
console.log(2);
}
function third() {
console.log(3);
}
let button = document.getElementById("button");
let container = document.getElementById("container");
button.addEventListener("click", first);
button.addEventListener("click", second);
container.addEventListener("click", third);
Давайте предположим, что мы хотели добавить третью функцию, которая выводит 3 на консоль. В этом сценарии мы также переместим вторую функцию, чтобы она также находилась на кнопке. Теперь мы нанесем третий слой на контейнер.
Короче говоря: у нас есть два обработчика событий на кнопке, и нажатие <div#container>
теперь выведет 3.
Это будет вести себя так же, как и раньше, и оно будет распространяться по дереву DOM и выводить 1, 2, 3 в таком порядке.
Использование Event.stopPropagation()
и Event.stopImmediatePropagation()
Добавление Event.stopPropagation()
к первой функции, например, так, выведет 1, 2 на консоль.
function first(event) {
event.stopPropagation();
console.log(1);
}
Это связано с тем, что Event.stopPropagation()
немедленно предотвратит запуск всех событий щелчка на родительском элементе, но это не остановит вызов любых других обработчиков событий.
Как вы можете видеть в нашем примере, у нашей кнопки есть два обработчика щелчков, и это остановило распространение на ее родителей. Если вы хотите остановить все обработчики событий после первого, вот где появляется Event.stopImmediatePropagation()
.
function first(event) {
event.stopImmediatePropagation();
console.log(1);
}
Теперь первая функция действительно остановит родительские обработчики щелчков и все остальные обработчики щелчков. Консоль теперь напечатает только 1, тогда как если бы мы просто использовали Event.stopPropagation()
, она напечатала бы 1, 2, и, не включая ни то, ни другое, напечатала бы 1, 2, 3.