Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Різниця між event.preventdefault() та event.stoppropagation()». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`event.preventDefault()`** скасовує вбудовану дію браузера для події. **`event.stopPropagation()`** зупиняє спливання (bubbling) події до батьківських елементів. ```javascript link.addEventListener('click', (e) => { e.preventDefault(); // браузер не перейде за посиланням }); button.addEventListener('click', (e) => { e.stopPropagation(); // обробники батьківських елементів не спрацюють }); ``` **Ключове:** методи незалежні. `stopPropagation` не блокує дії браузера, а `preventDefault` не зупиняє спливання.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`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 = true` | `event.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 і закриває модалку раніше, ніж користувач встигає зрозуміти що сталось.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.