preventDefault()

preventDefault() – метод объекта события в JavaScript, отменяющий действие браузера по умолчанию: переход по ссылке, отправку формы, отметку чекбокса. Само событие не останавливается и продолжает всплывать по DOM.

4 минуты чтения

У многих событий браузера есть действие по умолчанию: клик по ссылке ведёт на новый URL, кнопка отправляет форму с перезагрузкой страницы, клик по чекбоксу ставит галочку, mousedown с движением мыши выделяет текст, contextmenu открывает меню браузера. Метод event.preventDefault() отменяет именно это действие – и только его: событие продолжает всплывать по DOM, поэтому делегирование событий не ломается. Метод не принимает аргументов, возвращает undefined, работает во всех современных браузерах (Baseline Widely available, с июля 2015) и в Web Workers.

Как это работает

Обработчик вызывает 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 нужен явно.

Чек-лист диагностики:

  1. Событие отменяемо? Проверьте event.cancelable: у неотменяемых событий preventDefault() не даёт эффекта.
  2. Слушатель не пассивный? Для wheel и touch-событий на документе passive включён по умолчанию – добавьте { passive: false }.
  3. То ли событие? Действие по умолчанию привязано к конкретному событию: галочку чекбокса отменяют в обработчике 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.

Материалы по теме

Валентина Меланина

Нужна консультация?

Разберу ваш сайт и покажу точки роста

Если хотите понять, как этот термин применить к вашему проекту — начнём с аудита.