У многих событий браузера есть действие по умолчанию: клик по ссылке ведёт на новый URL, кнопка отправляет форму с перезагрузкой страницы, клик по чекбоксу ставит галочку, mousedown с движением мыши выделяет текст, contextmenu открывает меню браузера. Метод event.preventDefault() отменяет именно это действие – и только его: событие продолжает всплывать по DOM, поэтому делегирование событий не ломается. Метод не принимает аргументов, возвращает undefined, работает во всех современных браузерах (Baseline Widely available, с июля 2015) и в Web Workers.
preventDefault()
Определение
preventDefault() – метод объекта события в JavaScript, отменяющий действие браузера по умолчанию: переход по ссылке, отправку формы, отметку чекбокса. Само событие не останавливается и продолжает всплывать по DOM.
4 мин 41 Обновлено 5 сентября
Как это работает
Обработчик вызывает event.preventDefault() – браузер получает сигнал «событие обработано явно, действие по умолчанию не выполнять». Три канонических сценария:
// ссылка: попап или SPA-роутер вместо перехода
link.addEventListener('click', (event) => {
event.preventDefault();
openPopup(link.href);
});
// форма: не отправлять, пока не пройдена валидация
form.addEventListener('submit', (event) => {
if (!isFormValid()) event.preventDefault();
});
// чекбокс: запретить переключение
checkbox.addEventListener('click', (event) => event.preventDefault());У события есть два служебных свойства. event.cancelable показывает, отменяемо ли действие в принципе: на неотменяемом событии вызов preventDefault() просто игнорируется. event.defaultPrevented возвращает true, если действие уже отменено, – так обработчики выше по цепочке всплытия узнают, что событие обработал другой код, и не дублируют логику. Типовой паттерн: обработчик contextmenu на document сначала проверяет if (event.defaultPrevented) return – и не перекрывает кастомные меню, которые уже отменили действие ниже по дереву.
preventDefault, stopPropagation и stopImmediatePropagation – в чём разница
| Метод | Что отменяет | Что продолжается |
|---|---|---|
| preventDefault() | Действие браузера по умолчанию | Событие всплывает дальше, все обработчики выполняются |
| stopPropagation() | Дальнейшее распространение: событие не дойдёт до предков | Действие браузера и остальные обработчики текущего элемента |
| stopImmediatePropagation() | Распространение и остальные обработчики на том же элементе | Действие браузера по умолчанию |
Методы независимы и не заменяют друг друга: preventDefault() управляет поведением браузера, два других – маршрутом события по DOM. Если нужно и то и другое, их вызывают вместе.
return false – три разных поведения
Классический источник путаницы при переносе старого кода:
- jQuery – return false из обработчика автоматически вызывает и preventDefault(), и stopPropagation(): два действия сразу (прямая формулировка документации api.jquery.com). После переноса такого кода на нативный JS «внезапно» ломается делегирование.
- HTML-атрибут – в onclick="return false" возврат false отменяет действие по умолчанию.
- addEventListener – return false не делает ничего: действие отменяется только явным preventDefault().
Зачем это нужно
Типовые задачи: SPA-навигация без перезагрузки страницы, попап по клику на ссылку с data-атрибутом, клиентская валидация формы перед отправкой, кастомное контекстное меню, фильтрация вводимых символов, блокировка скролла под модальным окном. Логика везде одна: браузерное поведение заменяется собственным. На Битрикс-сайтах это ежедневная практика: попап обратного звонка вместо перехода по ссылке, AJAX-отправка формы без перезагрузки страницы, шторка корзины поверх каталога – всё начинается с preventDefault() в первом же обработчике.
Два ограничения на практике. Первое – доступность: ссылка с отменённым переходом для скринридера остаётся ссылкой; если элемент ведёт себя как кнопка, семантически честнее использовать button. Второе – MDN рекомендует сначала искать декларативные альтернативы: атрибуты disabled и readonly вместо перехвата ввода, встроенную HTML-валидацию форм (constraint validation) вместо ручного перехвата submit, CSS overflow вместо блокировки прокрутки скриптом. Атрибуты required, pattern и type="email" отсекают невалидные данные без единой строки JavaScript. Меньше кода – меньше точек отказа.
Пример
Самый частый практический вопрос – «почему preventDefault() не работает». Разберём главный случай: блокировку скролла на мобильных под открытой модалкой.
// НЕ работает: слушатель пассивный, preventDefault()
// игнорируется, в консоли – предупреждение
document.addEventListener('touchmove',
(e) => e.preventDefault(), { passive: true });
// Работает: passive отключён явно
document.addEventListener('touchmove',
(e) => e.preventDefault(), { passive: false });Опция passive: true декларирует, что обработчик не будет отменять действие, поэтому браузер запускает скролл сразу, не дожидаясь JavaScript, – это убирает подтормаживания прокрутки. Ловушка в значении по умолчанию: по спецификации passive равен false, но все браузеры, кроме Safari, включают его автоматически для wheel, mousewheel, touchstart и touchmove на window, document и document.body. Поэтому блокировка скролла без явного { passive: false } молча не работает в Chrome и Firefox. Та же история со свайпами в галереях и на картах: обработчику touchstart или touchmove, который должен подавить нативный жест, passive: false нужен явно.
Чек-лист диагностики:
- Событие отменяемо? Проверьте event.cancelable: у неотменяемых событий preventDefault() не даёт эффекта.
- Слушатель не пассивный? Для wheel и touch-событий на документе passive включён по умолчанию – добавьте { passive: false }.
- То ли событие? Действие по умолчанию привязано к конкретному событию: галочку чекбокса отменяют в обработчике click – в change действие уже произошло.
Быстрая проверка в конце: выведите event.defaultPrevented сразу после вызова. true – отмена дошла до браузера; false – сработал один из пунктов выше.
Частые вопросы
Что делает preventDefault() простыми словами?
Отменяет действие, которое браузер выполнил бы в ответ на событие: переход по ссылке, отправку формы, отметку чекбокса, показ контекстного меню. Само событие при этом не останавливается и продолжает всплывать по DOM. Метод вызывают внутри обработчика: event.preventDefault().
Чем preventDefault() отличается от stopPropagation()?
preventDefault() отменяет действие браузера по умолчанию, но событие продолжает распространяться по DOM. stopPropagation() – наоборот: останавливает всплытие к предкам, но действие браузера выполняется. Методы независимы; если нужно и то и другое, их вызывают вместе.
Почему preventDefault() не работает?
Три частые причины: событие неотменяемое (проверьте event.cancelable), слушатель зарегистрирован с passive: true (для wheel и touch-событий на документе это значение по умолчанию во всех браузерах, кроме Safari), либо действие по умолчанию относится к другому событию. Диагностику начинайте с console.log(event.cancelable).
Что такое passive: true и почему он блокирует preventDefault()?
Это опция addEventListener, которой обработчик обещает не отменять действие по умолчанию. Браузер тогда запускает скролл сразу, не дожидаясь JavaScript, и прокрутка не подтормаживает. Вызов preventDefault() в пассивном слушателе игнорируется, а в консоль выводится предупреждение; чтобы реально блокировать скролл, укажите { passive: false } явно.
return false – это то же самое, что preventDefault()?
Нет, поведение зависит от контекста. В jQuery return false из обработчика вызывает сразу preventDefault() и stopPropagation(). В HTML-атрибуте onclick="return false" – отменяет действие по умолчанию. В нативном addEventListener return false не делает ничего: нужен явный preventDefault().
Как узнать, что действие по умолчанию уже отменено?
Через свойство event.defaultPrevented: после вызова preventDefault() оно возвращает true. Это удобно в цепочке всплытия – обработчик на предке проверяет флаг и не дублирует логику, если событие уже обработал вложенный код.
Когда preventDefault() лучше не использовать?
MDN рекомендует декларативные альтернативы, где они есть: атрибуты disabled и readonly вместо перехвата ввода, встроенную HTML-валидацию (constraint validation) вместо ручного перехвата submit, CSS overflow вместо блокировки прокрутки скриптом. Плюс доступность: если ссылка с отменённым переходом ведёт себя как кнопка, семантически правильнее элемент button.
Связанные термины
Материалы по теме
Что почитать дальше по этой теме