Skip to main content

Різниця між event.preventdefault() та event.stoppropagation()

preventDefault проти stopPropagation - один скасовує вбудовану реакцію браузера на подію, інший зупиняє спливання (bubbling) події до батьківських елементів DOM. Це два незалежних механізми, і виклик одного не впливає на інший.

Теорія

TL;DR

  • preventDefault() блокує те, що браузер робить після події: переходить за URL, відправляє форму, показує контекстне меню
  • stopPropagation() блокує, куди подія рухається далі: вгору через батьківські елементи
  • Аналогія: preventDefault = ігнорування дзвінка у двері (дзвінок був, але ніхто не відкриває); stopPropagation = звукоізоляція стін (сусіди взагалі не чують)
  • stopPropagation сам по собі не зупинить відправку форми. Для цього потрібен preventDefault.
  • Правило вибору: небажана поведінка браузера → preventDefault; небажаний ланцюжок обробників → stopPropagation

Швидкий приклад

javascript
document.getElementById('parent').addEventListener('click', () => { console.log('Батько спрацював'); }); document.getElementById('child').addEventListener('click', (e) => { e.stopPropagation(); // Обробник батька не запуститься console.log('Дочірній елемент спрацював'); }); // З stopPropagation: тільки "Дочірній елемент спрацював" // Без нього: "Дочірній елемент спрацював", потім "Батько спрацював"

Батьківський обробник мовчить завдяки stopPropagation. Виклик preventDefault тут нічого б не змінив, бо кнопка всередині <div> не має жодної вбудованої дії браузера.

Головна різниця

preventDefault() каже браузеру пропустити вбудовану відповідь: не переходити за посиланням, не перезавантажувати сторінку при відправці форми. Після цього виклику подія продовжує нормально подорожувати деревом DOM. stopPropagation() обрізає маршрут події, тому батьківські обробники не спрацьовують, але вбудована дія браузера виконується, якщо тільки ти також не викликав preventDefault(). Це два окремих прапорці на одному об'єкті події.

Коли використовувати

  • Тег <a> із власним роутингом замість стандартного переходу → preventDefault
  • Форма, що відправляє дані через fetch замість перезавантаження сторінки → preventDefault
  • Кнопка всередині картки, де картка теж має обробник кліку → stopPropagation на кнопці
  • Модальне вікно з фоновим закриттям: клік всередині не повинен його закривати → stopPropagation на контенті модалки
  • Кастомний дропдаун поверх посилання → обидва, спочатку preventDefault

Таблиця порівняння

АспектpreventDefault()stopPropagation()
Впливає наВбудовану дію браузераРух події через DOM
Обхід DOMНе змінюється, батьки спрацьовуютьЗаблокований, батьки пропускаються
Встановлює прапорецьevent.defaultPrevented = trueevent.cancelBubble = true
Типове використання<a> без навігації, форма без перезавантаженняФон модалки, вкладені обробники
Вплив на інший методВідсутнійВідсутній

Як браузер це обробляє

Коли подія виникає, вона проходить три фази: захоплення (від кореня до цільового елементу), target (на самому елементі), спливання (назад до кореня). preventDefault встановлює прапорець, який браузер перевіряє після завершення всього dispatch-циклу, перед виконанням вбудованої дії. stopPropagation виходить з циклу обробки раніше, тому обробники фази спливання більше не запускаються. Важливий нюанс: якщо батьківський слухач зареєстровано з { capture: true }, він запускається ще до обробника дочірнього елементу. Виклик stopPropagation в дочірньому елементі не скасує вже виконане.

Типові помилки

Помилка 1: очікування, що stopPropagation заблокує відправку форми

javascript
// Неправильно: форма все одно відправиться submitButton.addEventListener('click', (e) => { e.stopPropagation(); }); // Правильно submitButton.addEventListener('click', (e) => { e.preventDefault(); });

stopPropagation керує маршрутом події, а не поведінкою браузера.

Помилка 2: кілька обробників на одному елементі

javascript
elem.addEventListener('click', fn1); elem.addEventListener('click', fn2); // fn2 все одно запуститься, навіть якщо fn1 викликала stopPropagation

stopPropagation зупиняє рух події до інших елементів. Для блокування обробників на тому ж елементі використовуй stopImmediatePropagation().

Помилка 3: модалка без stopPropagation на контенті

javascript
// Баг: будь-який клік всередині модалки спливає до overlay і викликає closeModal overlay.addEventListener('click', closeModal); // Виправлення modalContent.addEventListener('click', (e) => e.stopPropagation());

Де зустрічається в реальних проектах

  • React: e.stopPropagation() в onClick для дропдаунів і вкладених компонентів; Vue має скорочення @click.stop і @click.prevent
  • Обробники форм у SPA-застосунках: e.preventDefault() перед викликом fetch
  • jQuery: .preventDefault() і .stopPropagation() як окремі методи-обгортки
  • Shadow DOM: stopPropagation не перетинає межі shadow tree за замовчуванням

Питання на співбесіді

Q: Яка різниця між stopPropagation і stopImmediatePropagation?
A: stopPropagation зупиняє подію від потрапляння до батьківських елементів, але інші обробники на тому ж елементі в тій же фазі продовжують виконуватись. stopImmediatePropagation блокує обидва: і поточний елемент, і батьківські.

Q: Чи працює preventDefault у фазі захоплення?
A: Так. preventDefault не залежить від фази. Браузер перевіряє прапорець після завершення всього dispatch-циклу, тому виклик у будь-якій фазі дає однаковий результат.

Q: Чи можна викликати preventDefault на кастомній події?
A: Так, якщо подію створено з { cancelable: true }. Без цього параметру preventDefault не матиме ефекту, а event.defaultPrevented залишиться false.

Q: У React e.stopPropagation() зупиняє нативне DOM-спливання?
A: Ні. React використовує синтетичні події зі своїм механізмом делегування. e.stopPropagation() зупиняє спливання лише в межах React-дерева. Для блокування нативного DOM використовуй e.nativeEvent.stopImmediatePropagation().

Q: У Shadow DOM stopPropagation перетинає межу тіньового дерева?
A: Ні. Події перенацілюються на межах shadow DOM і залишаються в межах свого дерева. Щоб навмисно пробити межу, використовуй CustomEvent з параметром { composed: true }.

Приклади

Блокування переходу за посиланням

html
<a href="https://example.com" id="link">Перейти</a>
javascript
document.getElementById('link').addEventListener('click', (e) => { e.preventDefault(); console.log('Клік зафіксовано, навігацію заблоковано'); // Сторінка не змінюється, запускається власна логіка });

Подія виникла і спливла деревом DOM нормально. Скасовано лише вбудовану відповідь браузера. Батьківські обробники кліку, якщо такі є, отримали б цю подію без проблем.

Модальне вікно в React із stopPropagation

jsx
function Modal({ onClose }) { return ( <div className="overlay" onClick={onClose}> <div className="content" onClick={(e) => e.stopPropagation()}> <p>Клік тут не закриє модалку</p> <button onClick={onClose}>Закрити</button> </div> </div> ); } // Клік по .content: stopPropagation не дає спливти до .overlay, onClose не викликається // Прямий клік по .overlay: onClose спрацьовує

Це один з найпоширеніших патернів для модальних компонентів, і пропущений stopPropagation на контенті є класичним багом, який я виловлював у продакшені. Клік спливає до overlay і закриває модалку раніше, ніж користувач встигає зрозуміти що сталось.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?