← Назад к вопросам
Как работает всплытие событий в браузере?
1.2 Junior🔥 122 комментариев
#JavaScript Core
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Всплытие событий (Event Bubbling) в браузере
Всплытие событий - это фундаментальный механизм DOM, благодаря которому события распространяются от целевого элемента вверх по дереву к его родителям. Это один из ключевых концептов для понимания взаимодействия в JavaScript.
Как работает всплытие
Когда событие происходит на элементе, браузер проходит три фазы:
1. Capturing phase (фаза захвата) - от window к целевому элементу
2. Target phase (целевая фаза) - на самом элементе
3. Bubbling phase (фаза всплытия) - от целевого элемента обратно к window
<div id="parent">
<button id="child">Нажми меня</button>
</div>
<script>
const parent = document.getElementById('parent');
const child = document.getElementById('child');
// Capturing phase (true = захвата)
parent.addEventListener('click', () => {
console.log('Parent capturing');
}, true);
// Target phase (на самом элементе)
child.addEventListener('click', () => {
console.log('Child target');
});
// Bubbling phase (всплытие, false по умолчанию)
parent.addEventListener('click', () => {
console.log('Parent bubbling');
}, false);
// Когда кликнем на кнопку, выведет:
// Parent capturing (фаза захвата)
// Child target (целевая фаза)
// Parent bubbling (фаза всплытия)
</script>
Практический пример
<div class="container">
<div class="menu">
<button class="item">Профиль</button>
<button class="item">Настройки</button>
<button class="item">Выход</button>
</div>
</div>
<script>
const container = document.querySelector('.container');
// Вместо добавления слушателя на каждую кнопку,
// добавляем один слушатель на родителя
container.addEventListener('click', (e) => {
if (e.target.classList.contains('item')) {
console.log('Клик на кнопку:', e.target.textContent);
// Обработать клик
}
});
// Это называется Event Delegation (делегирование)
// Преимущества:
// - Меньше памяти (один слушатель вместо трёх)
// - Динамические элементы будут работать
// - Более чистый код
</script>
Остановка всплытия (stopPropagation)
Можно остановить всплытие события:
const child = document.getElementById('child');
const parent = document.getElementById('parent');
child.addEventListener('click', (e) => {
console.log('Child clicked');
e.stopPropagation(); // Остановить всплытие
});
parent.addEventListener('click', () => {
console.log('Parent clicked'); // Не выполнится
});
stopImmediatePropagation
Остановить не только всплытие, но и другие слушатели на том же элементе:
button.addEventListener('click', (e) => {
console.log('Слушатель 1');
e.stopImmediatePropagation();
});
button.addEventListener('click', (e) => {
console.log('Слушатель 2'); // Не выполнится
});
preventDefault
Отменить действие браузера (но событие всё ещё всплывает):
const form = document.querySelector('form');
form.addEventListener('submit', (e) => {
e.preventDefault(); // Не отправить форму
console.log('Форма не отправлена');
// Сделать кастомную обработку
fetch('/api/submit', {
method: 'POST',
body: new FormData(form)
});
});
События, которые НЕ всплывают
Не все события всплывают:
const events = {
'Всплывают': ['click', 'dblclick', 'keydown', 'keyup', 'input', 'change', 'submit'],
'НЕ всплывают': ['focus', 'blur', 'load', 'unload', 'scroll', 'resize', 'mouseenter', 'mouseleave']
};
button.addEventListener('focus', (e) => {
console.log('Focus не всплывает');
});
button.addEventListener('mouseenter', (e) => {
console.log('Mouseenter не всплывает'); // Используй mouseover для всплытия
});
Проверка наличия всплытия
button.addEventListener('click', (e) => {
console.log('bubbles:', e.bubbles); // true
console.log('cancelable:', e.cancelable); // true (можно отменить)
});
button.addEventListener('focus', (e) => {
console.log('bubbles:', e.bubbles); // false
});
Создание кастомных событий с всплытием
const customEvent = new CustomEvent('myEvent', {
bubbles: true, // Событие будет всплывать
cancelable: true, // Можно отменить
detail: { message: 'Данные события' }
});
element.addEventListener('myEvent', (e) => {
console.log('Моё событие:', e.detail.message);
});
element.dispatchEvent(customEvent);
Практический паттерн: Модальное окно
<div id="modal" class="modal hidden">
<div class="modal-content">
<h2>Подтверждение</h2>
<p>Вы уверены?</p>
<button class="close">Закрыть</button>
</div>
</div>
<script>
const modal = document.getElementById('modal');
// Закрыть модаль при клике на фон
modal.addEventListener('click', (e) => {
if (e.target === modal) { // Клик на фон, не на содержимое
modal.classList.add('hidden');
}
});
// Закрыть при клике на кнопку
modal.querySelector('.close').addEventListener('click', () => {
modal.classList.add('hidden');
});
// Содержимое не закроется при клике внутри
// потому что .modal-content не === modal
</script>
Производительность: Event Delegation
// ❌ Плохо: слушатель на каждом элементе
const buttons = document.querySelectorAll('button');
buttons.forEach(button => {
button.addEventListener('click', handleClick);
});
// Проблемы:
// - 1000 кнопок = 1000 слушателей
// - Новые кнопки не будут работать
// - Много памяти
// ✅ Хорошо: делегирование
document.addEventListener('click', (e) => {
if (e.target.matches('button')) {
handleClick(e);
}
});
// Преимущества:
// - Всего 1 слушатель
// - Работает с динамическими элементами
// - Меньше памяти
Отладка всплытия
const element = document.getElementById('test');
const originalStopPropagation = Event.prototype.stopPropagation;
Event.prototype.stopPropagation = function() {
console.trace('stopPropagation вызван'); // Увидим стек вызовов
originalStopPropagation.call(this);
};
Всплытие событий - это мощный механизм, который позволяет писать эффективный и чистый код. Понимание фаз всплытия, делегирования событий и методов контроля необходимо для работы с DOM.