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

Что такое всплытие?

1.3 Junior🔥 211 комментариев
#HTML и CSS

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

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

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

Что такое всплытие (Event Bubbling)

Всплытие (bubbling) — это механизм распространения событий в DOM от элемента, где оно произошло, вверх к корневому элементу. Это один из трёх этапов обработки событий в JavaScript и важен для понимания делегирования событий.

Три этапа обработки событий

Событие проходит три фазы:

  1. Capturing (перехват) — событие идёт от document к элементу
  2. Target (цель) — событие достигает целевого элемента
  3. Bubbling (всплытие) — событие идёт от элемента обратно к document
┌─────────────────────────────────────────┐
│            document                     │
│  ↓ (capturing) ↑ (bubbling)             │
│  ┌─────────────────────────────────┐   │
│  │        html                      │   │
│  │  ↓ (capturing) ↑ (bubbling)     │   │
│  │  ┌──────────────────────────┐   │   │
│  │  │      body                 │   │   │
│  │  │  ↓ (capturing) ↑ (bubble)│   │   │
│  │  │  ┌──────────────────┐    │   │   │
│  │  │  │  div (target)    │    │   │   │
│  │  │  │  ↓ click event   │    │   │   │
│  │  │  └──────────────────┘    │   │   │
│  │  └──────────────────────────┘   │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

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

Давайте посмотрим, как событие click всплывает вверх:

<div id="parent" style="border: 2px solid blue; padding: 20px;">
  <button id="child">Нажми на кнопку</button>
</div>
const parent = document.getElementById('parent');
const child = document.getElementById('child');

// Слушатель на кнопке
child.addEventListener('click', (event) => {
  console.log('3. Клик на кнопку');
});

// Слушатель на родительском div
parent.addEventListener('click', (event) => {
  console.log('2. Событие всплыло на parent div');
});

// Слушатель на document
document.addEventListener('click', (event) => {
  console.log('1. Событие достигло document');
});

// Вывод при клике на кнопку:
// 3. Клик на кнопку
// 2. Событие всплыло на parent div
// 1. Событие достигло document

event.target vs event.currentTarget

Важно различать эти два свойства:

parent.addEventListener('click', (event) => {
  console.log(event.target);       // <button> (где произошло событие)
  console.log(event.currentTarget); // <div>    (где слушатель)
});

// При клике на кнопку:
// event.target = <button id="child">
// event.currentTarget = <div id="parent">

Остановка всплытия

child.addEventListener('click', (event) => {
  event.stopPropagation(); // Событие не пойдёт выше
  console.log('Клик на кнопку');
});

parent.addEventListener('click', (event) => {
  console.log('На parent'); // Это НЕ выполнится
});

// Вывод: только "Клик на кнопку"

stopImmediatePropagation()

Этот метод не только останавливает всплытие, но и другие слушатели на текущем элементе:

button.addEventListener('click', (event) => {
  event.stopImmediatePropagation();
  console.log('Первый слушатель');
});

button.addEventListener('click', (event) => {
  console.log('Второй слушатель'); // Это НЕ выполнится!
});

// Вывод: только "Первый слушатель"

preventDefault()

Отличается от stopPropagation(): не останавливает всплытие, а отменяет действие по умолчанию:

link.addEventListener('click', (event) => {
  event.preventDefault(); // Не переходить по ссылке
  console.log('Ссылка не откроется');
});

// Событие всё ещё всплывает!
parent.addEventListener('click', (event) => {
  console.log('Событие всё ещё идёт вверх'); // Это ВЫПОЛНИТСЯ
});

Элементы, которые НЕ имеют всплытия

Не все события имеют всплытие:

// События БЕЗ всплытия:
- focus (но есть focusin с всплытием)
- blur (но есть focusout с всплытием)
- load / unload
- scroll
- resize
- reset
- submit
- mouseenter / mouseleave (но есть mouseover/mouseout с всплытием)
- mouseout / mouseover (разные!)
- media события (play, pause)

// Проверить, имеет ли событие всплытие:
const click = new MouseEvent('click');
console.log(click.bubbles); // true

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

Делегирование событий (Event Delegation)

Всплытие позволяет реализовать мощный паттерн — делегирование:

<ul id="list">
  <li>Элемент 1</li>
  <li>Элемент 2</li>
  <li>Элемент 3</li>
  <!-- Динамически добавляются элементы -->
</ul>
// ❌ Неправильно: слушатель только на существующих элементах
const items = document.querySelectorAll('li');
items.forEach(item => {
  item.addEventListener('click', handler);
});
// Новые элементы не будут работать!

// ✅ Правильно: слушатель на родителе
const list = document.getElementById('list');
list.addEventListener('click', (event) => {
  if (event.target.tagName === 'LI') {
    console.log('Клик на:', event.target.textContent);
  }
});

// Теперь новые элементы работают автоматически!
const newItem = document.createElement('li');
newItem.textContent = 'Элемент 4';
list.appendChild(newItem); // Клик на него всё равно сработает!

Реальный пример: обработка кликов по кнопкам

<div class="toolbar">
  <button data-action="save">Сохранить</button>
  <button data-action="delete">Удалить</button>
  <button data-action="edit">Редактировать</button>
</div>
const toolbar = document.querySelector('.toolbar');

const actions = {
  save: () => console.log('Сохранение...'),
  delete: () => console.log('Удаление...'),
  edit: () => console.log('Редактирование...')
};

toolbar.addEventListener('click', (event) => {
  // Проверяем, кликнули ли на кнопку
  if (event.target.tagName === 'BUTTON') {
    const action = event.target.dataset.action;
    const handler = actions[action];
    if (handler) {
      handler();
    }
  }
});

Производительность

Делегирование событий экономит память и ресурсы:

// ❌ Плохо: 1000 слушателей
const items = document.querySelectorAll('.item'); // 1000 элементов
items.forEach(item => {
  item.addEventListener('click', handler); // 1000 слушателей!
});

// ✅ Хорошо: 1 слушатель
const container = document.querySelector('.container');
container.addEventListener('click', (event) => {
  if (event.target.classList.contains('item')) {
    handler(event);
  }
});

Ключевые выводы

  • Всплытие — процесс распространения события вверх по DOM
  • Не все события имеют всплытие (focus, blur, load и т.д.)
  • event.stopPropagation() останавливает всплытие
  • event.target — элемент, где произошло событие
  • event.currentTarget — элемент с слушателем
  • Делегирование событий использует всплытие для эффективной обработки
  • Делегирование экономит память и работает с динамическими элементами