Интервью-вопрос
Что такое preventDefault в JavaScript
preventDefault отменяет стандартное действие браузера у события, если его можно отменить. Важно не путать это со всплытием и не ломать формы, ссылки, прокрутку и доступность интерфейса.
- Добавлен
- Редакция
Подготовьте короткий ответ и пару деталей на случай уточняющих вопросов.
Мини-квиз
Проверка перед разбором
Несколько быстрых вопросов перед разбором. Так проще поймать места, которые только кажутся понятными.
Вопрос 1 из 50 правильно
Разбор
Разобраться, а не зазубрить
Дальше разбираем суть, типичные уточнения и места, где легко сказать лишнее или перепутать термины.
Базовая идея
У многих событий в браузере есть стандартное действие. У ссылки это переход по адресу. У формы это отправка. У правого клика это контекстное меню. У некоторых клавиш это ввод, прокрутка или submit формы.
event.preventDefault() говорит браузеру: стандартное действие выполнять не нужно. Само событие при этом не исчезает. Другие обработчики могут выполниться. Событие может всплыть к родителям, а флаг event.defaultPrevented станет true.
Короткий ответ на интервью удобно строить так: сначала скажите, какое поведение отменяется. Потом отдельно скажите, чего метод не делает.
preventDefault отменяет стандартное действие браузера для события, например отправку формы или переход по ссылке. Он не останавливает всплытие. Если нужно остановить всплытие, это отдельная задача для stopPropagation.
Когда его стоит вызывать
Критерий простой. Вызывайте preventDefault, когда вы осознанно берете стандартное поведение под контроль и можете заменить его понятным сценарием.
Например, форма отправляется через API, а не обычной перезагрузкой страницы. Тогда вы отменяете стандартную отправку, валидируете данные, показываете ошибки и делаете запрос.
function SignupForm() {
const [status, setStatus] = React.useState("idle");
const [error, setError] = React.useState("");
async function handleSubmit(event) {
event.preventDefault();
if (status === "loading") return;
const form = event.currentTarget;
const data = new FormData(form);
const email = String(data.get("email") || "").trim();
if (!email) {
setError("Укажите email");
return;
}
setStatus("loading");
setError("");
try {
const response = await fetch("/api/signup", {
method: "POST",
body: data,
});
if (!response.ok) {
throw new Error("Request failed");
}
setStatus("success");
} catch {
setError("Не удалось отправить форму. Попробуйте еще раз.");
setStatus("idle");
}
}
return (
<form onSubmit={handleSubmit}>
<label htmlFor="email">Email</label>
<input
id="email"
name="email"
type="email"
aria-describedby={error ? "email-error" : undefined}
/>
{error && <p id="email-error">{error}</p>}
<button type="submit" disabled={status === "loading"}>
{status === "loading" ? "Sending..." : "Send"}
</button>
</form>
);
}Практический вывод такой. Отмена стандартного действия должна идти вместе с явным следующим шагом. Иначе пользователь нажмет кнопку, ничего не произойдет, а интерфейс будет выглядеть сломанным. В форме сразу подумайте про ошибку, loading, повторный submit и доступную связь ошибки с полем. Если API авторизуется cookie, не отключайте CSRF-защиту только потому, что запрос идет через fetch.
Как решить, нужен ли preventDefault
Вызывайте preventDefault и сразу реализуйте замену.Используйте stopPropagation. preventDefault тут не решает задачу.Отменяйте submit, а не только click по кнопке.Лучше заменить ее на button, а не постоянно отменять переход.Проверьте passive listener и cancelable. Иначе preventDefault может быть проигнорирован.Разница со всплытием
preventDefault не управляет маршрутом события по DOM. Если вы кликнули по ссылке внутри карточки, отмена перехода по ссылке не остановит обработчик карточки.
card.addEventListener("click", () => {
console.log("card click");
});
link.addEventListener("click", (event) => {
event.preventDefault();
console.log("link click");
});В этом примере браузер не перейдет по ссылке, но обработчик карточки все равно сработает при всплытии. Если именно это нежелательно, нужен отдельный вызов stopPropagation. Но не добавляйте его автоматически. Родительские обработчики могут отвечать за аналитику, закрытие меню или фокус.
Формы и риск двойной логики
С формами частая ошибка такая: отменить клик по кнопке, но не обработать сам submit. Это выглядит рабочим, пока пользователь нажимает только мышью. Потом он нажимает Enter, и форма уходит обычным способом.
Еще одна ловушка: после preventDefault вызвать form.submit() как будто это безопасное продолжение. Такой вызов может обходить событие submit и встроенную валидацию браузера. Если вам нужно инициировать нормальную отправку формы программно, чаще подходит requestSubmit(). Но не вызывайте его без защиты прямо из того же submit-обработчика. Иначе легко получить цикл или двойной запрос. Если вы отправляете через API, делайте это явно и контролируйте loading, ошибку и повторную отправку.
- 1Слушать событие submit у формы
- 2Сразу вызвать preventDefault, если отправку берет на себя JS
- 3Проверить данные и показать ошибки рядом с полями
- 4Отправить запрос через API или отдельно запустить нативный submit без цикла
- 5Показать loading, ошибку, успех и заблокировать повторную отправку
- 1Слушать только click по кнопке
- 2Забыть про Enter и другие способы отправки формы
- 3Отменить поведение и не показать пользователю понятную ошибку
- 4Не показать loading и разрешить повторный клик
- 5Вызвать form.submit и случайно обойти валидацию
- 6Получить двойной запрос или потерю введенных данных
Отменяемые события, passive listeners и defaultPrevented
Не каждое событие можно отменить. Для проверки есть свойство event.cancelable. Если оно равно false, вызов preventDefault не отменит стандартное действие.
Для производительности браузеры используют passive listeners. В таком обработчике вы как бы обещаете, что не будете отменять стандартное действие. Если внутри passive listener вызвать preventDefault, браузер проигнорирует вызов. Это особенно важно для wheel, touchstart и touchmove, потому что от них зависит плавность прокрутки.
window.addEventListener(
"touchmove",
(event) => {
if (event.cancelable) {
event.preventDefault();
}
},
{ passive: false }
);Такой код не стоит копировать без причины. Блокировка прокрутки может ухудшить UX. Сначала проверьте, нельзя ли решить задачу через CSS, например touch-action, или через нормальную структуру модального окна.
Практический вывод для ответа
На интервью не нужно читать лекцию про весь Event API. Достаточно дать точное определение, назвать отличие от всплытия и показать, что вы понимаете риск.
Хорошая короткая формулировка:
preventDefault отменяет стандартное действие браузера у события, например переход по ссылке или отправку формы. Он не останавливает всплытие, поэтому stopPropagation это отдельный инструмент. Я использую preventDefault, когда сам реализую замену стандартного поведения, например валидирую форму и отправляю данные через API.
Если хотите усилить ответ, добавьте один нюанс: метод работает только для отменяемых событий, а в passive listener может быть проигнорирован. Это показывает практический опыт с браузером, а не только знание определения.
Частые ошибки
Где обычно ошибаются
Проверьте формулировки, которые звучат уверенно, но на интервью быстро выдают пробелы.
- 1
Путать с остановкой всплытия
preventDefaultне мешает событию дойти до родительских элементов. Если родительский обработчик закрывает модалку или выбирает строку таблицы, он все равно может сработать. Для управления распространением нуженstopPropagation. Его тоже лучше применять точечно. - 2
Отменять поведение без замены
Если отменить отправку формы и ничего не сделать дальше, пользователь не поймет, что произошло. Если отменить переход по ссылке, навигация сломается. В хорошем ответе лучше сразу добавить, какое поведение вы реализуете вместо стандартного. - 3
Вешать обработчик только на кнопку формы
Кнопка не единственный способ отправить форму. Пользователь может нажать Enter, автозаполнение может инициировать submit, а код может вызватьrequestSubmit. Поэтому контроль формы обычно ставят на событиеsubmit. - 4
Игнорировать cancelable и passive
Некоторые события нельзя отменить. А в passive listener вызовpreventDefaultбудет проигнорирован. Это часто ломает попытки блокировать прокрутку на мобильных устройствах. Если вам правда нужно отменять прокрутку, проверьте настройки слушателя и используйте CSS вродеtouch-action, когда это подходит. - 5
Использовать ссылку как кнопку
Ссылка сhref="#"и постояннымpreventDefaultухудшает семантику и доступность. Если элемент выполняет действие, обычно нуженbutton. Если элемент ведет на URL, оставьте нормальную ссылку и отменяйте переход только при реальной необходимости.
Follow-up
Что могут спросить дальше
Короткие ответы на вопросы, которыми проверяют понимание событий, форм и стандартного поведения браузера.
Живые ответы
Видео с похожим вопросом
Если найдем публичные интервью с таким вопросом, добавим их сюда. Их удобно смотреть после теории, чтобы свериться с живыми ответами.
Пока видео нет. Когда появятся подходящие публичные интервью, добавим их в этот блок, чтобы можно было сравнить разбор с тем, как отвечают реальные кандидаты.
Что такое IndexedDB 😎
IndexedDB - асинхронное клиентское хранилище для структурированных данных, индексов и оффлайн-сценариев. Разбираем, когда оно уместно, чем отличается от localStorage и какие ошибки ломают фронтенд.
Что такое WebSocket
Разбор вопроса «Что такое WebSocket» для Frontend Developer: что проверяет интервьюер, ключевые тезисы, практические примеры и частые ошибки.