В современных браузерах есть много действий по умолчанию: кликаете по ссылке — начинается переход, нажимаете Enter в форме — запускается отправка и перезагрузка страницы. Для старого веба это работало отлично, но сейчас, когда половина UI живёт на JavaScript, такая самодеятельность браузера нередко ломает логику приложения.
Чтобы притормозить браузер, в JavaScript есть метод event.preventDefault(). В этой статье разберём, как перехватывать управление у браузера, почему использовать return false — плохая привычка из прошлого и чем отмена действия отличается от остановки всплытия события.
Что такое preventDefault в JavaScript и зачем он нужен
Браузеры создавались так, чтобы быть максимально полезными и автономными. У них есть сотни встроенных сценариев поведения — действий по умолчанию, которые срабатывают автоматически, когда пользователь что-то делает.
Например:
- Кликнули по ссылке
<a>— и браузер переходит по новому адресу. - Нажали кнопку «Отправить» в форме — браузер собирает данные и перезагружает страницу.
- Нажали правую кнопку мыши — открывается контекстное меню браузера.
- Покрутили колёсико мыши — страница скроллится.
Но в современном вебе стандартное поведение может мешать. Например, в одностраничных приложениях (SPA) мы хотим, чтобы при клике по ссылке страница не перезагружалась, а подгружался контент через JavaScript. Или, например, в браузерных играх или сложных редакторах нам может понадобиться использовать правую кнопку мыши для своих целей, а не для открытия меню «Сохранить как...», ну и так далее.
И вот для этого-то нам и нужен preventDefault.
Важный момент: этот метод отменяет только физическое действие браузера — переход, отправку, прокрутку, — но не останавливает само событие. Сигнал о том, что «клик произошёл», продолжит всплывать вверх по DOM-дереву, и его смогут услышать другие скрипты. Для полной остановки события есть другой метод, но о нём мы поговорим позже.
Синтаксис и принцип работы метода preventDefault()
Метод preventDefault() нельзя вызывать «сам по себе» — он существует только у объекта события, который браузер передаёт вашему обработчику. У него нет глобальной версии вроде window.preventDefault().
Поэтому порядок всегда один и тот же: сначала вы ловите событие, браузер передаёт вам объект event, и уже у него вызываете event.preventDefault().
Как получить объект event
Когда браузер фиксирует событие — клик, нажатие клавиши или отправку формы, — он автоматически создаёт специальный объект Event. В этом объекте записано всё: по какому элементу кликнули, какие координаты у курсора, какая кнопка нажата.
Чтобы получить доступ к этому объекту, нам нужно просто объявить его как аргумент в нашей функции-обработчике, браузер сам передаст его туда первым параметром. Чаще всего разработчики называют этот аргумент event, evt или просто e.
element.addEventListener('click', function(event) {
// Теперь переменная event доступна внутри этой функции
// И мы можем управлять поведением браузера
});
Метод preventDefault() живёт именно на этом объекте. Ещё раз: чтобы отменить действие браузера, недостаточно просто написать preventDefault() — сначала нам нужно получить сам event и уже у него вызвать event.preventDefault().
Так что доступ к объекту события — это точка входа ко всему управлению поведением браузера.
Базовый пример использования
Самый распространённый пример — «ссылка-ловушка». Допустим, у нас есть тег <a>, который ведёт на внешний сайт, но нам нужно, чтобы он не переходил по адресу, а выполнял какую-то нашу логику в JavaScript.
const link = document.querySelector('a');
link.addEventListener('click', (e) => {
// Отменяем стандартное действие (переход по ссылке)
e.preventDefault();
// Выполняем наш код
console.log('Клик прошёл, но перехода не будет!');
});

Мы перехватили событие до того, как браузер успел выполнить своё стандартное действие — переход по ссылке.
Если бы строки e.preventDefault() не было, произошло бы следующее:
- Клик отработал бы;
console.logуспел бы мигнуть в консоли;- Браузер немедленно открыл бы новый адрес, перезагрузив страницу.
А метод preventDefault() заставляет браузер остановиться на нашем коде: событие сработало, обработчик выполнился, но переход отменён. То есть событие остаётся, а действие браузера — нет.
Полезный блок со скидкой
Если вам интересно разбираться со смартфонами, компьютерами и прочими гаджетами и вы хотите научиться создавать софт под них с нуля или тестировать то, что сделали другие, — держите промокод Практикума на любой платный курс: KOD (можно просто на него нажать). Он даст скидку при покупке и позволит сэкономить на обучении.
Бесплатные курсы в Практикуме тоже есть — по всем специальностям и направлениям, начать можно в любой момент, карту привязывать не нужно, если что.
Практические примеры использования preventDefault
Рассмотрим три самых частых сценария из жизни фронтендера.
Предотвращение отправки формы (form submit)
Когда форма ловит событие submit, браузер действует строго по старым правилам: собирает все значения <input>, <select>, <textarea>, формирует запрос и перезагружает страницу, будто перед нами сайт из 2005 года. Если в этот момент у вас в обработчике есть console.log, вы его, конечно, увидите, но только на долю секунды, перед тем как вкладка моргнёт и уйдёт в перезагрузку.
event.preventDefault() ломает этот сценарий: мы говорим браузеру не трогать страницу, ничего не отправлять и оставить всё как есть. После этого форма становится обычным контейнером данных и мы полностью контролируем, что будет дальше: сами читаем поля, отправляем запрос через fetch, сами обновляем интерфейс.
Выглядит это так:
const form = document.querySelector('.login-form');
form.addEventListener('submit', (event) => {
// Стоп! Не перезагружай страницу
event.preventDefault();
// Теперь мы сами соберём данные
const formData = new FormData(form);
// И сами отправим их на сервер через fetch
console.log('Данные улетели через AJAX, страница на месте');
});
После вызова event.preventDefault() браузер больше не запускает свой стандартный цикл отправки формы. Событие submit всё равно произошло, наш обработчик получил к нему доступ, но действие по умолчанию отключено.
Дальше форма перестаёт быть активным участником процесса и превращается просто в источник данных и мы сами решаем, что с ними делать.
Если бы строки event.preventDefault() не было, обработчик бы отработал, но сразу после этого страница бы перезагрузилась, и никакая наша логика не успела бы выполнить свою задачу.
Отмена перехода по ссылке (anchor click)
Базовый пример мы уже видели выше, теперь глянем на реальный UX-кейс. Часто preventDefault используют для защиты пользователя. Представьте, что человек заполнил огромную анкету, но случайно кликнул по ссылке «Главная». Если браузер сработает штатно — все данные пропадут.
Мы можем перехватить этот клик и запросить подтверждение:
const links = document.querySelectorAll('a');
// Допустим, есть несохраненные данные
let isUnsaved = true;
links.forEach(link => {
link.addEventListener('click', (event) => {
if (isUnsaved) {
// Отменяем переход
event.preventDefault();
// Спрашиваем пользователя
if (confirm('Вы точно хотите уйти? Данные пропадут!')) {
// Если он согласен — переходим вручную
window.location.href = link.href;
}
}
});
});

Что делает обработчик:
- Пользователь кликает по ссылке.
- Событие
clickсрабатывает до того, как браузер начнёт переход. - Мы проверяем, есть ли несохранённые данные.
- Если есть — вызываем
event.preventDefault(), тем самым блокируя стандартное действие<a>. - Показываем пользователю предупреждение через
confirm(). - Если он согласен уйти — вызываем переход вручную, через
window.location.href = link.href.
То есть preventDefault здесь срабатывает как пауза перед реальным действием. Без него браузер бы мгновенно ушёл на новый адрес, а мы бы не успели предупредить пользователя.
Такой паттерн часто используется в сложных интерфейсах: формах, админках, редакторах и любых сценариях, где уход со страницы может стоить потери данных.
Блокировка действия по умолчанию для кнопок
Здесь кроется подвох, о котором иногда забывают: согласно спецификации HTML, любая кнопка <button> внутри тега <form>, у которой явно не указан атрибут type, автоматически считается кнопкой отправки, то type="submit". То есть браузер воспринимает такую кнопку как «отправить форму».
А теперь представьте, что вы сделали форму регистрации и добавили туда сервисную кнопку «Показать пароль» или «Очистить поля». Пользователь нажимает на неё, а браузер думает: «Это кнопка в форме → значит, надо отправить форму». Тогда страница перезагружается, данные улетают или теряются, и пользователь в ярости.
Чтобы объяснить браузеру, что это просто кнопка, а не подтверждение отправки, у нас есть два пути:
- Через HTML: прописываем
type="button"и убираем всю двусмысленность. - Через JavaScript: перехватываем клик и блокируем поведение по умолчанию.
Например:
const showPassBtn = document.querySelector('.show-pass');
showPassBtn.addEventListener('click', (event) => {
// Блокируем попытку отправить форму, так как это просто сервисная кнопка
event.preventDefault();
// Переключаем input type="password" на "text"
togglePasswordVisibility();
});

С event.preventDefault() браузер больше не пытается подтверждать форму, и кнопка начинает вести себя как обычная интерактивная часть интерфейса. А дальше мы уже спокойно выполняем свой сценарий — переключаем отображение пароля, ничего не отправляя и не перезагружая страницу.
Но иногда можно заметить, что клик по такой кнопке не перезагружает страницу, а просто подсвечивает красным пустые поля. Это значит, что сработала встроенная HTML5-валидация. Браузер попытался отправить форму, увидел, что она не заполнена, и остановил процесс. Но это всё равно плохой UX. Пользователь хотел просто посмотреть пароль, а браузер начал ругаться и требовать заполнить логин. Сервисная кнопка не должна триггерить проверку формы.
Return false в JavaScript: как работает и когда использовать
Если вы гуглите решение какой-то проблемы с событиями, то на StackOverflow, особенно в ответах 2010–2014 годов, можно увидеть совет: «Просто напиши return false, и всё заработает».
Но нет. В современном ванильном JS это вредный совет. return false — это исторический артефакт, который ведёт себя совершенно по-разному в зависимости от того, как именно вы навесили событие на элемент.
Разница между return false и preventDefault
В современном стандарте return false не делает ничего. Вот вообще ничего, браузер его просто игнорирует.
Посмотрим на код, который не сработает. Допустим, есть ссылка, которая должна открываться в новом окне:
<a href="https://practicum.yandex.ru/" class="test-1" target="_blank">
И логика обработки, чтобы эта ссылка не открывалась:
document.querySelector('.test-1').addEventListener('click', (event) => {
console.log('addEventListener: return false вызван'');
return false;
});
Каким бы очевидным это ни казалось, браузер просто игнорирует возвращаемое значение обработчика, навешанного через addEventListener. Всё потому, что результат выполнения функции return false — это логика вашей программы, а отмена поведения браузера — часть API события, и это два разных мира.
Сработает это только в древних случаях: в инлайн-обработчиках (onclick="...") и при назначении обработчика через свойство (element.onclick = ...):
<a href="https://practicum.yandex.ru/" onclick="alert('Стоп'); return false;">
Я никуда не перейду
</a>

Самое главное, что нужно запомнить:
event.preventDefault()— это метод API события, который явно говорит: «Отмени действие». Он работает везде и всегда.return false— это результат выполнения функции. Браузер смотрит на него только в механизмахonclick, а вaddEventListenerэтот результат уходит в пустоту.
Особенности работы return false в разных ситуациях
Почему же столько людей уверены, что return false работает? Потому что они привыкли к библиотеке jQuery, где эта строчка была ядерной кнопкой и отменяла всё. Когда писали:
$('a').click(function() {
return false;
});
то библиотека jQuery под капотом делала сразу два действия:
event.preventDefault()— отменяла переход.event.stopPropagation()— останавливала всплытие события вверх.
Проблема в том, что разработчик привыкает к этому в jQuery, переходит на чистый JS или React/Vue, потом в обработчике пишет по привычке return false…, а ничего не происходит: ссылка открывается, форма отправляется. Поэтому в 2026 году правило простое: забудьте про return false и используйте явные методы.
Сравнение: preventDefault vs return false vs stopPropagation
Чтобы не гадать, какой метод выбрать, нужно запомнить одну простую вещь: события в JavaScript живут двойной жизнью:
- Всплытие: событие рождается на кнопке и летит вверх к
body. - Действие по умолчанию: браузер видит событие и делает то, что заложено в его программе, — перезагружает страницу.
Эти методы управляют разными аспектами этой жизни.
Когда использовать preventDefault
preventDefault() нужен в тех ситуациях, когда вам не нравится действие браузера, но само событие должно продолжить жить своей жизнью. То есть вы не хотите, чтобы ссылка открылась, форма подтвердилась или колесо мыши прокрутило страницу, — но сам факт клика или прокрутки должен быть виден другим обработчикам.
Важный момент: preventDefault() не влияет на всплытие событий. Событие по-прежнему поднимается вверх по DOM — родительские элементы узнают, что клик был, и смогут на него отреагировать.
Это часто важно в реальных интерфейсах:
- В SPA мы перехватываем клик по ссылке, чтобы не перезагружать страницу, а поменять контент через JavaScript. При этом глобальные обработчики, например закрытие выпадающих меню при клике в документ, всё равно должны сработать.
- В своих пользовательских контролах (слайдерах, виртуальных списках, кастомных кнопках) мы блокируем нативное поведение браузера, но оставляем логику событий нетронутой.
Когда использовать return false
В 2026 году — почти никогда.
Это не стандартный инструмент, а синтаксический сахар из прошлого, который бесполезен в чистом JS. В jQuery return false убивает всё живое — и действие браузера, и всплытие. Такое поведение слишком агрессивно и часто приводит к багам, когда часть интерфейса перестаёт реагировать.
Единственный случай, когда нужен return false, — это если вы поддерживаете легаси-код 10-летней давности с атрибутами onclick="..." в HTML.
Что такое stopPropagation и чем отличается от return false
С этим методом часто возникает путаница — из-за слова Stop можно подумать, что мы блокируем вообще всё. Но на самом деле stopPropagation не отменяет действие браузера, а занимается исключительно маршрутизацией события внутри вашего кода.
Вспомните, как работают события в JS: когда вы кликаете по кнопке, сигнал об этом сначала срабатывает на самой кнопке, а потом, как пузырёк воздуха, всплывает вверх по DOM-дереву — к родителю, контейнеру, body и, наконец, к window.

Метод stopPropagation() просто «лопает» этот пузырёк и говорит: «Событие произошло, мы его обработали, но родителям об этом знать не обязательно. Пусть это останется нашим маленьким секретом».
Если вы повесите stopPropagation() на обычную ссылку и кликните по ней, событие не дойдёт до body, но ссылка при этом откроется. Браузеру всё равно, всплыло событие или нет, он увидел клик и выполнил свою работу.
Типичный пример — это кнопка «лайк» внутри карточки. Допустим, есть карточка товара в интернет-магазине, и эта карточка сама по себе одна большая ссылка, клик по которой ведёт на страницу товара. Но внутри этой карточки есть маленькая кнопка «В избранное».
Возникает конфликт интересов:
- Пользователь хочет лайкнуть товар, не уходя со страницы.
- Но так как кнопка находится внутри карточки, клик по «В избранное» неизбежно всплывает вверх до самой карточки.
- В итоге срабатывает обработчик карточки и пользователя перекидывает на страницу товара.
Именно здесь необходим stopPropagation(). С помощью этого метода мы говорим кнопке «в избранное», чтобы она обработала лайк, но не пускала клик дальше к карточке.
// Логика карточки-родителя
card.addEventListener('click', () => {
console.log('Переходим на страницу товара...');
window.location.href = '/product/1';
});
// Логика лайка (потомок)
likeBtn.addEventListener('click', (event) => {
// Лопаем пузырёк события здесь. Родитель даже не узнает, что был клик
event.stopPropagation();
// Делаем своё дело
console.log('Товар добавлен в избранное, остаёмся на месте.');
});

А вот почему preventDefault здесь не поможет:
- Кнопка лайка сама по себе не имеет действия «перейти по ссылке», это просто
<button>, и отменять ей нечего. - Переход выполняет карточка-родитель — скрипт на
divили ссылка<a>вокруг. - Когда вы кликаете на лайк, то физически кликаете и на карточку тоже.
Поэтому единственный способ спастись — это остановить всплытие через stopPropagation. Мы как бы говорим: «Этот клик предназначался только для лайка, не рассказывай родительской карточке, что он произошёл».
Бонус для читателей
Если вам интересно погрузиться в мир ИТ и при этом немного сэкономить, держите наш промокод на курсы Практикума. Он даст вам скидку при оплате, поможет с льготной ипотекой и даст безлимит на маркетплейсах. Ладно, окей, это просто скидка, без остального, но хорошая.
Частые ошибки и особенности использования
Рассмотрим ситуации, где браузер может проигнорировать ваши намерения остановить действие по умолчанию.
Работа с асинхронными функциями
Допустим, мы хотим проверить данные на сервере перед отправкой формы.
- Пользователь нажимает «Отправить».
- Вы запускаете
async-функцию. - Ждёте ответ (
await). - Если сервер вернул ошибку, то отменяете отправку.
Но так не сработает: браузер не будет ждать завершения вашего await. Событие — это синхронный процесс, и как только интерпретатор доходит до первого await, а функция «засыпает», то браузер думает: «Ну, синхронный код закончился, запретов не было, значит отправляем форму».
Посмотрим на примере:
form.addEventListener('submit', async (e) => {
// Здесь ошибка: браузер не будет ждать этот запрос
const isAllowed = await checkOnServer();
if (!isAllowed) {
// Слишком поздно! Форма уже улетела, страница перезагрузилась
e.preventDefault();
}
});
Браузер уйдёт в перезагрузку раньше, чем ваш код проснётся после await. Поэтому всегда отменяйте действие сразу же в первой строчке. А если всё хорошо, то отправляйте форму программно.
form.addEventListener('submit', async (e) => {
// Так правильно: сначала блокируем, потом разбираемся
e.preventDefault();
const isAllowed = await checkOnServer();
if (isAllowed) {
// Или отправляем через fetch
form.submit();
}
});
Суть в том, что браузер не обязан ждать ваш асинхронный код. Он живёт в своём темпе и обрабатывает событие submit так же, как делал это двадцать лет назад, то есть без пауз.
Поэтому, если вы откладываете логику в await, но не отменили действие сразу, момент будет упущен. Отмена через e.preventDefault() работает только тогда, когда её видно синхронно, прямо в теле обработчика до первой «засыпающей» операции.
Блокируйте поведение в самом начале и управляйте процессом уже сами. Это и есть современный стандарт работы с формами в SPA и AJAX-приложениях.
Особенности в разных браузерах
Наверняка вы видели в консоли браузера предупреждение: Unable to preventDefault inside passive event listener. Это случается, когда вы пытаетесь заблокировать скролл — события wheel, touchstart, touchmove.

Причина проста: некоторые события — прежде всего wheel, touchstart и touchmove — современные браузеры по умолчанию помечают как пассивные (passive: true). Пассивный слушатель означает буквально следующее: «Я, браузер, обещаю пользователю, что прокрутка начнётся сразу, без задержки. Я не стану ждать, пока JavaScript решит, можно ли скроллить».
Это ускоряет интерфейс, особенно на мобильных устройствах, но создаёт побочный эффект: в пассивном слушателе запрещено отменять действие и preventDefault() просто не сработает. И если вам действительно нужно блокировать скролл, например при открытии модалки или свайп-жеста, слушатель должен быть активным — только тогда браузер разрешит отмену поведения.
Делается это так:
document.addEventListener('touchmove', (e) => {
// Теперь сработает, скролл заблокируется
e.preventDefault();
// Самое важное
}, { passive: false });
Когда preventDefault не работает
preventDefault() не универсальная кнопка отмены. Он срабатывает только тогда, когда у события вообще в принципе есть «действие по умолчанию», которое можно остановить до того, как оно произойдёт.
У некоторых событий действие уже совершилось к моменту вызова обработчика — и отменять там просто нечего. Типичный пример — scroll: прокрутка уже произошла, браузер лишь сообщает вам об этом, возвращать полосу прокрутки обратно он не обязан.
Чтобы понять, можно ли отменить конкретное событие, у объекта есть флаг:
link.addEventListener('click', (e) => {
if (e.cancelable) {
e.preventDefault(); // Можно отменять
} else {
console.log('Это событие неизбежно, как дедлайн');
}
});

На практике cancelable почти никто не проверяет вручную: разработчик просто вызывает preventDefault(), и, если браузер может отменить действие, он отменит. Но для общего понимания важно различать два типа событий:
- Отменяемые (
preventDefaultработает) — клик, клавиатурные события,mousedown,submit,touchstartиwheel, если слушатель не пассивный. - Неотменяемые (preventDefault бессилен) —
scroll,focus,blur,input— события, которые описывают уже случившийся факт, а не намерение.
Ищете работу в IT?
Карьерный навигатор Практикума разберёт ваше резюме, проложит маршрут к первому работодателю, подготовит к собеседованиям в 2026 году, а с января начнёт подбирать вакансии именно под вас.
Правильное использование preventDefault
Метод preventDefault позволяет полностью переписать стандартные сценарии поведения, но важно не увлечься и не сломать пользовательский опыт.
В каких случаях обязательно использовать
В современных SPA и вообще в динамических интерфейсах без preventDefault не обойтись. Если вы используете React, Vue, Angular или собираете собственный роутер, любое действие, которое по умолчанию ведёт к перезагрузке страницы, в том числе взаимодействие со ссылкой или формой, должно быть перехвачено. Иначе приложение просто не сможет поддерживать состояние, будет мигать и постоянно сбрасывать данные.
Точно так же preventDefault нужен там, где вы управляете поведением напрямую:
- drag-and-drop, чтобы браузер не открывал файл;
- кастомные жесты и горячие клавиши;
- игровые механики, где скролл страницы недопустим.
Правило простое: если вы берёте на себя управление интерфейсом, отключайте автопилот браузера.
Распространённые антипаттерны
Главный грех разработчика — ломать нативные функции без веской причины.
Не стоит блокировать контекстное меню через правый клик ради защиты контента — это только раздражает, а украсть картинку можно и через DevTools. Не блокируйте выделение текста или стандартные шорткаты, если только не пишите онлайн-редактор кода.
И конечно, не пишите return false. Это неявный, устаревший способ, который в современном коде часто не срабатывает или делает больше, чем вы планировали, останавливая ещё и всплытие. Чистый код должен быть предсказуемым: хотите отменить действие — пишите preventDefault().
