← Назад к вопросам

Что такое распространение события?

1.3 Junior🔥 241 комментариев
#Браузер и сетевые технологии

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Распространение события (Event Propagation / Event Bubbling)

Распространение события - это механизм, описывающий как браузер обрабатывает события, которые происходят на элементах DOM. Когда событие происходит на элементе, оно не останавливается на этом элементе, а распространяется по дереву DOM. За 10+ лет работы с JavaScript я вижу, что неправильное понимание этого механизма - частая причина ошибок.

Основная концепция

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

         document
             │
    ┌────────┼────────┐
    │        │        │
  html      body    (capture)
    │        │
    │      div.container
    │        │
    │      button   <- User clicked here
    │
  (bubbling phase)

Три фазы распространения события

1. Capturing Phase (Фаза перехвата)

Событие начинается с верхних элементов и движется вниз к целевому элементу:

document -> html -> body -> div.container -> button

2. Target Phase (Фаза цели)

Событие достигает элемента, на котором оно произошло:

button <- событие достигло цели

3. Bubbling Phase (Фаза всплытия)

Событие всплывает обратно вверх, от целевого элемента к корню:

button -> div.container -> body -> html -> document

Практический пример

<div class="container" id="container">
  <button id="btn">Click me</button>
</div>
const container = document.getElementById('container');
const btn = document.getElementById('btn');

// Capturing phase
console.log('1. Capturing');
document.addEventListener('click', () => {
  console.log('  document capturing');
}, true); // true = capturing phase

container.addEventListener('click', () => {
  console.log('  container capturing');
}, true);

btn.addEventListener('click', () => {
  console.log('  button capturing');
}, true);

// Target phase
btn.addEventListener('click', () => {
  console.log('2. Target phase - button');
});

// Bubbling phase
console.log('3. Bubbling');
btn.addEventListener('click', () => {
  console.log('  button bubbling');
});

container.addEventListener('click', () => {
  console.log('  container bubbling');
});

document.addEventListener('click', () => {
  console.log('  document bubbling');
});

// Вывод при клике на кнопку:
// 1. Capturing
//   document capturing
//   container capturing
//   button capturing
// 2. Target phase - button
// 3. Bubbling
//   button bubbling
//   container bubbling
//   document bubbling

Event Bubbling (Всплытие)

Это самая важная фаза в практической разработке.

Когда событие происходит на элементе, оно распространяется на родительские элементы:

<div class="parent">
  <div class="child">
    <button class="grandchild">Click me</button>
  </div>
</div>
const parent = document.querySelector('.parent');

// Этот обработчик вызовется, даже если клик был на button!
parent.addEventListener('click', (event) => {
  console.log('Parent clicked, event.target:', event.target);
  // event.target = <button class="grandchild">
  // event.currentTarget = <div class="parent">
});

Это полезно для делегирования событий (event delegation):

// Динамический список с удалением элементов
const list = document.querySelector('#todo-list');

list.addEventListener('click', (event) => {
  if (event.target.classList.contains('delete-btn')) {
    const item = event.target.closest('li');
    item.remove();
  }
});

// HTML
// <ul id="todo-list">
//   <li>Task 1 <button class="delete-btn">Delete</button></li>
//   <li>Task 2 <button class="delete-btn">Delete</button></li>
//   <li>Task 3 <button class="delete-btn">Delete</button></li>
// </ul>

// Преимущество: добавление новых элементов работает автоматически!
list.innerHTML += '<li>Task 4 <button class="delete-btn">Delete</button></li>';

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

event.stopPropagation() - останавливает всплытие

const button = document.querySelector('button');
const container = document.querySelector('.container');

button.addEventListener('click', (event) => {
  console.log('Button clicked');
  event.stopPropagation(); // Останавливаем всплытие!
});

container.addEventListener('click', (event) => {
  console.log('Container clicked'); // Это НЕ вызовется
});

// При клике на кнопку вывод:
// Button clicked
// (Container clicked не появится)

event.stopImmediatePropagation() - останавливает всё

const button = document.querySelector('button');

button.addEventListener('click', (event) => {
  console.log('First handler');
  event.stopImmediatePropagation();
});

button.addEventListener('click', (event) => {
  console.log('Second handler'); // Это НЕ вызовется
});

// Вывод:
// First handler

Какие события поддерживают bubbling?

События, которые всплывают:

- click, dblclick
- mousedown, mouseup, mousemove
- keydown, keyup, keypress
- input, change (частично)
- focus, blur (НЕ всплывают!)
- scroll, resize (НЕ всплывают!)
- load, unload (НЕ всплывают!)

Проверить, поддерживает ли событие bubbling:

const event = new Event('click');
console.log(event.bubbles); // true

const focusEvent = new Event('focus');
console.log(focusEvent.bubbles); // false

Практические примеры

1. Модальное окно с закрытием по клику на фон

const modal = document.querySelector('.modal');
const backdrop = document.querySelector('.modal-backdrop');

modal.addEventListener('click', (event) => {
  event.stopPropagation(); // Не закрываем при клике на модаль
});

backdrop.addEventListener('click', () => {
  modal.style.display = 'none'; // Закрываем при клике на фон
});

2. Делегирование на таблице

const table = document.querySelector('table');

table.addEventListener('click', (event) => {
  // Определяем, на что был клик
  if (event.target.tagName === 'BUTTON') {
    const row = event.target.closest('tr');
    const id = row.dataset.id;
    handleEdit(id);
  } else if (event.target.classList.contains('delete-btn')) {
    const row = event.target.closest('tr');
    const id = row.dataset.id;
    handleDelete(id);
  }
});

3. Форма с валидацией

const form = document.querySelector('#myForm');

form.addEventListener('submit', (event) => {
  event.preventDefault(); // Останавливаем отправку формы
  
  if (!validateForm()) {
    event.stopPropagation(); // Дополнительная защита
    return;
  }
  
  submitForm();
});

Важные замечания

1. event.target vs event.currentTarget

const parent = document.querySelector('.parent');

parent.addEventListener('click', (event) => {
  console.log(event.target);        // Элемент, на котором произошёл клик
  console.log(event.currentTarget);  // Элемент, на котором привязан слушатель (parent)
});

2. React и всплытие

// В React всплытие работает иначе (синтетические события)
function Parent() {
  return (
    <div onClick={() => console.log('Parent')}>  
      <button onClick={() => console.log('Button')}>
        Click me
      </button>
    </div>
  );
}
// Вывод: "Button", потом "Parent"

3. Асинхронность и события

const button = document.querySelector('button');

button.addEventListener('click', async (event) => {
  event.preventDefault(); // Сохраняется!
  await fetch('/api/data');
  // preventDefault остаётся в силе
});

Резюме

Распространение события - это процесс, при котором браузер обрабатывает события через три фазы: capturing, target и bubbling. Bubbling фаза наиболее важна и используется для делегирования событий. Понимание этого механизма критически для эффективной обработки событий в JavaScript и избежания типичных ошибок в обработке кликов и других пользовательских действий.