Gernar
Frontend DeveloperBrowser API

Интервью-вопрос

Что такое preventDefault в JavaScript

preventDefault отменяет стандартное действие браузера у события, если его можно отменить. Важно не путать это со всплытием и не ломать формы, ссылки, прокрутку и доступность интерфейса.

Добавлен
Редакция

Подготовьте короткий ответ и пару деталей на случай уточняющих вопросов.

🐰0
🥚0

Мини-квиз

Проверка перед разбором

Несколько быстрых вопросов перед разбором. Так проще поймать места, которые только кажутся понятными.

Вопрос 1 из 50 правильно

Как лучше сказать про отличие от stopPropagation?

Вы сейчас объясняете разницу коротко, без лишней теории.

Варианты ответа

Разбор

Разобраться, а не зазубрить

Дальше разбираем суть, типичные уточнения и места, где легко сказать лишнее или перепутать термины.

Базовая идея

У многих событий в браузере есть стандартное действие. У ссылки это переход по адресу. У формы это отправка. У правого клика это контекстное меню. У некоторых клавиш это ввод, прокрутка или 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

1Есть стандартное действие браузера, которое вам точно нужно заменить?
Вызывайте preventDefault и сразу реализуйте замену.
2Нужно только не пустить событие к родителю?
Используйте stopPropagation. preventDefault тут не решает задачу.
3Это форма, а вы хотите валидировать данные или отправить их через API?
Отменяйте submit, а не только click по кнопке.
4Это ссылка, которая запускает действие, а не ведет по URL?
Лучше заменить ее на button, а не постоянно отменять переход.
5Это scroll, wheel или touch обработчик?
Проверьте 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, ошибку и повторную отправку.

Безопасный submit
  1. 1Слушать событие submit у формы
  2. 2Сразу вызвать preventDefault, если отправку берет на себя JS
  3. 3Проверить данные и показать ошибки рядом с полями
  4. 4Отправить запрос через API или отдельно запустить нативный submit без цикла
  5. 5Показать loading, ошибку, успех и заблокировать повторную отправку
Опасный submit
  1. 1Слушать только click по кнопке
  2. 2Забыть про Enter и другие способы отправки формы
  3. 3Отменить поведение и не показать пользователю понятную ошибку
  4. 4Не показать loading и разрешить повторный клик
  5. 5Вызвать form.submit и случайно обойти валидацию
  6. 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. 1

    Путать с остановкой всплытия

    preventDefault не мешает событию дойти до родительских элементов. Если родительский обработчик закрывает модалку или выбирает строку таблицы, он все равно может сработать. Для управления распространением нужен stopPropagation. Его тоже лучше применять точечно.
  2. 2

    Отменять поведение без замены

    Если отменить отправку формы и ничего не сделать дальше, пользователь не поймет, что произошло. Если отменить переход по ссылке, навигация сломается. В хорошем ответе лучше сразу добавить, какое поведение вы реализуете вместо стандартного.
  3. 3

    Вешать обработчик только на кнопку формы

    Кнопка не единственный способ отправить форму. Пользователь может нажать Enter, автозаполнение может инициировать submit, а код может вызвать requestSubmit. Поэтому контроль формы обычно ставят на событие submit.
  4. 4

    Игнорировать cancelable и passive

    Некоторые события нельзя отменить. А в passive listener вызов preventDefault будет проигнорирован. Это часто ломает попытки блокировать прокрутку на мобильных устройствах. Если вам правда нужно отменять прокрутку, проверьте настройки слушателя и используйте CSS вроде touch-action, когда это подходит.
  5. 5

    Использовать ссылку как кнопку

    Ссылка с href="#" и постоянным preventDefault ухудшает семантику и доступность. Если элемент выполняет действие, обычно нужен button. Если элемент ведет на URL, оставьте нормальную ссылку и отменяйте переход только при реальной необходимости.

Follow-up

Что могут спросить дальше

Короткие ответы на вопросы, которыми проверяют понимание событий, форм и стандартного поведения браузера.

Живые ответы

Видео с похожим вопросом

Если найдем публичные интервью с таким вопросом, добавим их сюда. Их удобно смотреть после теории, чтобы свериться с живыми ответами.

Пока видео нет. Когда появятся подходящие публичные интервью, добавим их в этот блок, чтобы можно было сравнить разбор с тем, как отвечают реальные кандидаты.

Содержание