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

Фазы события

Прежде чем углубляться в особенности захвата и всплытия, важно понять три этапа события:

  1. Фаза захвата — это первая фаза события, когда оно начинается с самого внешнего элемента и спускается к целевому элементу. На этом этапе браузер проверяет, зарегистрированы ли на каком-либо из предков целевого элемента прослушиватели событий.
  2. Целевая фаза — это вторая фаза, когда событие отправляется целевому элементу, элементу, который фактически инициировал событие.
  3. Фаза всплытия — это третья фаза, когда событие всплывает от целевого элемента к самому внешнему элементу. На этом этапе браузер проверяет, зарегистрированы ли на каком-либо из предков целевого элемента прослушиватели событий.

Пример пузыря

Давайте начнем с примера, демонстрирующего, как работает всплывающее окно событий. Рассмотрим следующий HTML-код:

<div id="outer">
  <div id="middle">
    <div id="inner"></div>
  </div>
</div>

Теперь давайте прикрепим прослушиватель событий к каждому из элементов с помощью JavaScript:

const outer = document.getElementById('outer');
const middle = document.getElementById('middle');
const inner = document.getElementById('inner');

outer.addEventListener('click', () => {
  console.log('Outer clicked!');
});
middle.addEventListener('click', () => {
  console.log('Middle clicked!');
});
inner.addEventListener('click', () => {
  console.log('Inner clicked!');
});

Когда мы нажимаем на элемент inner, событие всплывает до элементов middle и outer. В итоге в консоли видим следующий вывод:

Inner clicked!
Middle clicked!
Outer clicked!

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

Пример захвата

Теперь давайте посмотрим на пример, демонстрирующий, как работает захват событий. Рассмотрим следующий HTML-код:

<div id="outer">
  <div id="middle">
    <div id="inner"></div>
  </div>
</div>

Давайте прикрепим прослушиватель событий к каждому из элементов с помощью JavaScript, но на этот раз с использованием фазы захвата:

const outer = document.getElementById('outer');
const middle = document.getElementById('middle');
const inner = document.getElementById('inner');

outer.addEventListener('click', () => {
  console.log('Outer clicked in capturing phase!');
}, true);
middle.addEventListener('click', () => {
  console.log('Middle clicked in capturing phase!');
}, true);
inner.addEventListener('click', () => {
  console.log('Inner clicked in capturing phase!');
}, true);

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

Outer clicked in capturing phase!
Middle clicked in capturing phase!
Inner clicked in capturing phase!

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

Остановка распространения событий

Иногда мы можем захотеть остановить распространение события, чтобы предотвратить дальнейшее всплытие или захват события. Мы можем сделать это, используя метод stopPropagation() объекта события. Например:

<div id="outer">
  <div id="inner"></div>
</div>

<script>
const outer = document.getElementById('outer');
const inner = document.getElementById('inner');

outer.addEventListener('click', () => {
  console.log('Outer clicked!');
});

inner.addEventListener('click', (event) => {
  console.log('Inner clicked!');
  event.stopPropagation(); // stop event propagation
});
</script>

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

Однако мы использовали метод stopPropagation() внутри прослушивателя событий click внутреннего div, чтобы предотвратить дальнейшее распространение события. Это означает, что событие щелчка внешнего div не будет срабатывать, когда мы нажимаем на внутренний div.

В результате, когда мы нажимаем на внутренний div, мы видим в консоли следующий вывод:

Inner clicked!

И событие щелчка внешнего div не запускается, как ожидалось.

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

Также важно отметить, что вы можете использовать метод stopPropagation() для предотвращения дальнейшего распространения события, что может быть полезно для предотвращения ненужной обработки событий или циклов событий.

В целом, понимание того, как работает распространение событий в дереве DOM, является важным аспектом обработки событий JavaScript и может помочь вам писать более эффективный и действенный код.