Что такое всплывающее и захватывающее событие?

В чем разница между всплытием и захватом событий? Какая модель быстрее и лучше использовать?

11 ответов

Решение

Бублинг и захват событий - это два способа распространения событий в 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());
}

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>
<section id="log"></section>

Еще один пример на JSFiddle.

Описание:

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.

Я нашел этот учебник на javascript.info очень ясным в объяснении этой темы. И его резюме из 3 пунктов в конце действительно говорит о важных моментах. Я цитирую это здесь:

  1. События сначала фиксируются до самой глубокой цели, а затем всплывают. В IE<9 они только пузырьковые.
  2. Все обработчики работают на стадии пузырьков, за исключением addEventListener с последним аргументом true, который является единственным способом отловить событие на этапе захвата.
  3. Пузырьки / захват могут быть остановлены с помощью event.cancelBubble=true (IE) или event.stopPropagation() для других браузеров.

DOM Events описывает 3 фазы распространения события: Фаза захвата - событие переходит к элементу. Целевая фаза - событие достигло целевого элемента. Фаза пузыря - событие всплывает из элемента.

Там также 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>

кипение

  Event propagate to the Upto root element is Bubbling.

Захватив

  Event propogate from body(root) element to eventTriggered Element is Capturing.

Когда браузер обнаруживает событие, он пытается найти обработчик события. Этот процесс имеет 3 фазы. Допустим, у нас есть эти элементы

         <body>
      <div>
        <button>click</button>
      </div>
    </body>

1-фаза захвата

Браузер взглянет на элемент, по которому только что щелкнули. Затем он перейдет к верхнему родительскому элементу, который является . если в теле есть какой-либо дескриптор клика, браузер вызовет его. после проверки элемента body он будет смотреть на второй верхний родительский элемент, который является element. Этот процесс будет повторяться до тех пор, пока браузер не перейдет к элементу кнопки в самом низу. Как только он увидит кнопку, первый этап захвата будет завершен. Большую часть времени мы игнорируем этот этап. Этот код игнорирует эту фазу

      document.addEventListener('click',handleClick)

Если вы хотите задействовать этот этап, вы должны написать это

      document.addEventListener('click',handleClick,true)

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

2-целевая фаза

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

3- Фаза пузыря

Напротив, на этом этапе браузер начнет процесс с непосредственного родительского элемента, которыйэлемент в этом случае, а затем он посетитэлемент. Этот код настроит обработчик событий для фазы пузырька.

      document.addEventListener('click',handleClick,false)

Я привел небольшой пример, где вы можете испытать «всплывание событий» вживую:

https://codepen.io/abernier/pen/yKGJXK?editors=1010

Щелкните любой элемент div, и вы увидите всплывающее событие «click»!

      $divs.forEach(($div) => $div.addEventListener("click", handleClick2));

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

Я хотел указать, что для самого внутреннего элемента может показаться что-то странное. Действительно, в этом случае порядок , в котором добавлены слушатели событий имеет значения .

В следующем примере захват для div2

будет выполняться раньше, чем всплытие; пока пузырится div4 будет выполняться первым, чем захват.

Это процесс распространения событий в JavaScript.

Всплытие событий: при этом событие сначала фиксируется и обрабатывается самым внутренним элементом, в котором оно произошло, затем оно распространяется через родительские элементы в иерархии DOM.

Захват событий. В этом случае событие сначала захватывается и обрабатывается самым внешним элементом, а затем распространяется вниз.

Другие вопросы по тегам