В чем разница между всплытием и захватом событий? Когда следует использовать пузыри против захвата?
В чем разница между всплытием и захватом событий? Когда следует использовать пузыри против захвата?
Ответы:
Пузырьки и захват событий - это два способа распространения событий в API DOM HTML, когда событие происходит в элементе внутри другого элемента, и оба элемента зарегистрировали дескриптор этого события. Режим распространения события определяет, в каком порядке элементы получают событие .
При пузырьковании событие сначала захватывается и обрабатывается самым внутренним элементом, а затем распространяется на внешние элементы.
При захвате событие сначала захватывается самым внешним элементом и распространяется на внутренние элементы.
Захват также называется «каплей», что помогает запомнить порядок распространения:
сочиться
В прежние времена Netscape выступал за захват событий, в то время как Microsoft поощряла всплытие событий. Оба являются частью стандарта событий объектной модели документа W3C (2000).
IE <9 использует только всплывающие события , а IE9 + и все основные браузеры поддерживают оба. С другой стороны, производительность всплытия событий может быть немного ниже всплывающих для сложных DOM.
Мы можем использовать addEventListener(type, listener, useCapture)
для регистрации обработчиков событий либо в режиме пузырьков (по умолчанию), либо в режиме захвата. Чтобы использовать модель захвата, передайте третий аргумент как true
.
<div>
<ul>
<li></li>
</ul>
</div>
В приведенной выше структуре предположим, что в li
элементе произошло событие щелчка .
В модели захвата событие будет обрабатываться div
сначала (сначала div
будут срабатывать обработчики щелчка в событии ), затем в ul
, затем в последнем в целевом элементе li
.
В пузырьковой модели произойдет обратное: сначала событие будет обрабатываться элементом li
, затем элементом ul
и, наконец, div
элементом.
Для получения дополнительной информации см.
В приведенном ниже примере, если вы щелкнете по любому из выделенных элементов, вы увидите, что сначала происходит фаза захвата потока распространения события, а затем фаза пузырьков.
var logElement = document.getElementById('log');
function log(msg) {
logElement.innerHTML += ('<p>' + msg + '</p>');
}
function capture() {
log('capture: ' + this.firstChild.nodeValue.trim());
}
function bubble() {
log('bubble: ' + this.firstChild.nodeValue.trim());
}
function clearOutput() {
logElement.innerHTML = "";
}
var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', capture, true);
divs[i].addEventListener('click', bubble, false);
}
var clearButton = document.getElementById('clear');
clearButton.addEventListener('click', clearOutput);
p {
line-height: 0;
}
div {
display:inline-block;
padding: 5px;
background: #fff;
border: 1px solid #aaa;
cursor: pointer;
}
div:hover {
border: 1px solid #faa;
background: #fdd;
}
<div>1
<div>2
<div>3
<div>4
<div>5</div>
</div>
</div>
</div>
</div>
<button id="clear">clear output</button>
<section id="log"></section>
useCapture
теперь поддерживается в IE> = 9. source
triclkling
же, как capturing
? Крокфорд рассказывает Trickling v. Bubbling
в этом видеообращении - youtube.com/watch?v=Fv9qT9joc0M&list=PL7664379246A246CB вокруг 1 hr 5 minutes
.
trickle down
= = onElement
=>bubble up
Описание:
quirksmode.org имеет хорошее описание этого. В двух словах (скопировано из quirksmode):
Захват событий
Когда вы используете захват событий
| | ---------------| |----------------- | element1 | | | | -----------| |----------- | | |element2 \ / | | | ------------------------- | | Event CAPTURING | -----------------------------------
обработчик события element1 срабатывает первым, обработчик события element2 срабатывает последним.
Событие кипит
Когда вы используете всплывающее событие
/ \ ---------------| |----------------- | element1 | | | | -----------| |----------- | | |element2 | | | | | ------------------------- | | Event BUBBLING | -----------------------------------
обработчик события element2 срабатывает первым, обработчик события element1 срабатывает последним.
Что использовать?
Это зависит от того, что вы хотите сделать. Там нет лучше. Разница заключается в порядке выполнения обработчиков событий. Большую часть времени будет нормально запускать обработчики событий в фазе пузырьков, но может также потребоваться запускать их раньше.
Если есть два элемента, элемент 1 и элемент 2. Элемент 2 находится внутри элемента 1, и мы присоединяем обработчик событий к обоим элементам, скажем, onClick. Теперь, когда мы нажмем на элемент 2, будет выполнен eventHandler для обоих элементов. Теперь здесь вопрос в том, в каком порядке будет выполняться событие. Если событие, связанное с элементом 1, выполняется первым, это называется захватом события, а если событие, связанное с элементом 2, выполняется первым, это называется всплывающим событием. Согласно W3C, событие начнется в фазе захвата, пока не достигнет цели, вернется к элементу, а затем начнет пузыриться.
Состояния захвата и всплытия известны параметром useCapture метода addEventListener.
eventTarget.addEventListener (тип, слушатель, [, useCapture]);
По умолчанию useCapture имеет значение false. Это означает, что он находится в пузырчатой фазе.
var div1 = document.querySelector("#div1");
var div2 = document.querySelector("#div2");
div1.addEventListener("click", function (event) {
alert("you clicked on div 1");
}, true);
div2.addEventListener("click", function (event) {
alert("you clicked on div 2");
}, false);
#div1{
background-color:red;
padding: 24px;
}
#div2{
background-color:green;
}
<div id="div1">
div 1
<div id="div2">
div 2
</div>
</div>
Пожалуйста, попробуйте изменить true и false.
the event will start in the capturing phase untill it reaches the target comes back to the element and then it starts bubbling
. Я только обнаружил, что addEventListener имеет параметр, useCapture
который может быть установлен в true или false; а в HTML 4.0 слушатели событий были указаны как атрибуты элемента и useCapture defaults to false
. Не могли бы вы дать ссылку на спецификацию, которая подтверждает то, что вы написали?
Я нашел этот учебник на javascript.info очень ясным в объяснении этой темы. И его резюме из 3 пунктов в конце действительно говорит о важных моментах. Я цитирую это здесь:
- События сначала фиксируются до самой глубокой цели, а затем всплывают. В IE <9 они только пузырьковые.
- Все обработчики работают на исключениях этапа пузырька
addEventListener
с последним аргументомtrue
, который является единственным способом отловить событие на этапе захвата.- Пузырьки / захват могут быть остановлены
event.cancelBubble=true
(IE) илиevent.stopPropagation()
для других браузеров.
Также есть Event.eventPhase
свойство, которое может сообщить вам, находится ли событие в назначении или откуда-то еще.
Обратите внимание, что совместимость браузера еще не определена. Я тестировал его на Chrome (66.0.3359.181) и Firefox (59.0.3), и он там поддерживается.
Расширяя и без того отличный фрагмент из принятого ответа , это вывод с использованием eventPhase
свойства
var logElement = document.getElementById('log');
function log(msg) {
if (logElement.innerHTML == "<p>No logs</p>")
logElement.innerHTML = "";
logElement.innerHTML += ('<p>' + msg + '</p>');
}
function humanizeEvent(eventPhase){
switch(eventPhase){
case 1: //Event.CAPTURING_PHASE
return "Event is being propagated through the target's ancestor objects";
case 2: //Event.AT_TARGET
return "The event has arrived at the event's target";
case 3: //Event.BUBBLING_PHASE
return "The event is propagating back up through the target's ancestors in reverse order";
}
}
function capture(e) {
log('capture: ' + this.firstChild.nodeValue.trim() + "; " +
humanizeEvent(e.eventPhase));
}
function bubble(e) {
log('bubble: ' + this.firstChild.nodeValue.trim() + "; " +
humanizeEvent(e.eventPhase));
}
var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', capture, true);
divs[i].addEventListener('click', bubble, false);
}
p {
line-height: 0;
}
div {
display:inline-block;
padding: 5px;
background: #fff;
border: 1px solid #aaa;
cursor: pointer;
}
div:hover {
border: 1px solid #faa;
background: #fdd;
}
<div>1
<div>2
<div>3
<div>4
<div>5</div>
</div>
</div>
</div>
</div>
<button onclick="document.getElementById('log').innerHTML = '<p>No logs</p>';">Clear logs</button>
<section id="log"></section>