Что такое всплытие и перехват событий?
В чем разница между всплытием события и его захватом? Когда следует использовать всплытие, а когда захват?
5 ответ(ов)
Когда у нас есть два элемента, элемент 1 и элемент 2, где элемент 2 содержится внутри элемента 1, и мы прикрепляем обработчик событий (например, onClick
) к обоим элементам, возникает интересный вопрос о порядке выполнения обработчиков при клике на элемент 2. При щелчке на элемент 2 обработчики событий обоих элементов выполнятся. Вопрос в том, в каком порядке они будут выполняться. Если обработчик элемента 1 выполняется первым, это называется захватом событий (event capturing). Если же обработчик элемента 2 выполняется первым, это называется всплытием событий (event bubbling).
Согласно спецификации W3C, событие начинается в фазе захвата до тех пор, пока не достигнет целевого элемента, а затем возвращается к родительскому элементу, начиная фазу всплытия.
Фазы захвата и всплытия определяются параметром useCapture
метода addEventListener
.
eventTarget.addEventListener(type, listener[, useCapture]);
По умолчанию useCapture
имеет значение false
, что означает, что событие находится в фазе всплытия.
В вашем коде:
var div1 = document.querySelector("#div1");
var div2 = document.querySelector("#div2");
div1.addEventListener("click", function (event) {
alert("вы нажали на div 1");
}, true); // Обработчик будет выполнен в фазе захвата
div2.addEventListener("click", function (event) {
alert("вы нажали на div 2");
}, false); // Обработчик будет выполнен в фазе всплытия
Если изменить true
на false
во втором слушателе или false
на true
в первом, то порядок выполнения изменится.
Пример HTML-кода:
<div id="div1">
div 1
<div id="div2">
div 2
</div>
</div>
Когда вы щелкните по элементу 2 (div2
), сначала выполнится обработчик на div1
с useCapture = true
, если вы оставите его так, а затем уже обработчик на div2
с useCapture = false
. Если вы поменяете параметры местами, то порядок изменится. Попробуйте поиграть с параметрами true
и false
, чтобы самостоятельно увидеть разницу в порядке выполнения.
Всплытие (Bubbling)
Событие распространяется от целевого элемента до корневого элемента и называется **ВСПЛЫТИЕМ**.
Захват (Capturing)
Событие распространяется от корневого элемента (например, body) к целевому элементу и называется **ЗАХВАТОМ**.
Когда браузер обнаруживает событие, он пытается найти обработчик событий. Этот процесс состоит из трех фаз. Рассмотрим следующий пример элементов:
<body>
<div>
<button>click</button>
</div>
</body>
1. Фаза захвата (Capture Phase)
Браузер сначала обращается к элементу, который был только что нажат. Затем он поднимается к верхнему родительскому элементу, которым является body
. Если на body
есть обработчик для события клика, браузер вызовет его. После проверки элемента body
он перейдет к следующему родительскому элементу, которым является div
. Этот процесс будет повторяться, пока браузер не дойдет до элемента кнопки на самом низу. Как только браузер увидит элемент кнопки, фаза захвата завершится. В большинстве случаев мы игнорируем эту фазу. Вот пример кода, который ее игнорирует:
document.addEventListener('click', handleClick);
Чтобы задействовать эту фазу, нужно написать так:
document.addEventListener('click', handleClick, true);
Эта фаза нужна, когда необходимо определить клик вне целевого элемента. Например, если у нас открыто выпадающее меню или модальное окно, и мы хотим его закрыть, если пользователь кликнет в любое место вне этого модального окна или выпадающего меню.
2. Фаза цели (Target Phase)
Браузер обращается к элементу, на который произошел клик, в данном случае к button
, и если на кнопке есть обработчик событий, он его вызовет.
3. Фаза всплытия (Bubble Phase)
В противоположность фазе захвата, на этой фазе браузер начинает процесс с непосредственного родительского элемента, которым в данном случае является div
, и затем переходит к элементу body
. Вот пример кода, который устанавливает обработчик событий для фазы всплытия:
document.addEventListener('click', handleClick, false);
Таким образом, обработка события проходит в три фазы: захват, цель и всплытие.
Как уже упоминали другие, всплытие и захват описывают порядок, в котором некоторые вложенные элементы получают заданное событие.
Я хотел бы обратить внимание на то, что для внутреннего элемента может возникнуть нечто странное. Действительно, для некоторых браузеров (например, Mozilla Firefox) порядок, в котором добавляются слушатели событий, имеет значение.
В следующем примере захват для div2
будет выполнен раньше, чем всплытие, в то время как всплытие для div4
будет выполнено раньше, чем захват.
function addClickListener(msg, num, type) {
document.querySelector("#div" + num)
.addEventListener("click", () => alert(msg + num), type);
}
bubble = (num) => addClickListener("bubble ", num, false);
capture = (num) => addClickListener("capture ", num, true);
// сначала захват, затем всплытие
capture(1);
capture(2);
bubble(2);
bubble(1);
// попробуем в обратном порядке
bubble(3);
bubble(4);
capture(4);
capture(3);
#div1, #div2, #div3, #div4 {
border: solid 1px;
padding: 3px;
margin: 3px;
}
<div id="div1">
div 1
<div id="div2">
div 2
</div>
</div>
<div id="div3">
div 3
<div id="div4">
div 4
</div>
</div>
Замечание: попробуйте запустить приведённый выше код в Mozilla Firefox.
EDIT: К 2024 году эта проблема, похоже, была решена.
В языке JavaScript процесс распространения событий можно разделить на два основных механизма: всплытие событий (Event Bubbling) и захват событий (Event Capturing).
Всплытие событий (Event Bubbling): В этой модели событие изначально обрабатывается наиболее вложенным элементом, на котором оно произошло. Затем оно "всплывает" наверх по иерархии DOM, передаваясь каждому родительскому элементу, пока не достигнет корневого элемента.
Захват событий (Event Capturing): В противоположность всплытию, в этом случае событие сначала захватывается и обрабатывается внешним элементом (самым верхним в иерархии DOM), после чего оно "пропускается" вниз, передаваясь всем дочерним элементам до того, на котором само событие произошло.
Таким образом, в зависимости от того, как вы настроите обработчики событий, может быть использован один из этих механиков для управления тем, как события обрабатываются в вашем приложении.
event.preventDefault() против return false: в чем разница?
Остановить вызов setInterval в JavaScript
Получение ID элемента, вызвавшего событие
Как запустить JavaScript после загрузки страницы?
Преимущества addEventListener по сравнению с onclick