</> Praktum

Главная / JavaScript / Формы в JavaScript

Справочник · JavaScript

Формы в JavaScript: от submit до fetch

Редакция PraktumОбновлено 6 мин чтенияУровень: новичок

Чтобы обработать форму скриптом, слушают событие submit у самой формы, отменяют перезагрузку через preventDefault() и собирают данные через new FormData(form). FormData сам берёт все поля с атрибутом name, включая чекбоксы и файлы. Отправить его можно напрямую через fetch, а превратить в обычный объект — через Object.fromEntries.

Коротко
  • Слушай submit формы, а не click кнопки.
  • new FormData(form) — все поля с name.
  • Object.fromEntries(formData) — в объект.
  • fetch(url, { method: 'POST', body: formData }).
  • Блокируй кнопку на время отправки, сообщай результат.

Минимальный пример

<form id="feedback" action="/api/feedback" method="post">
  <label for="name">Имя</label>
  <input id="name" name="name" required>
  <label for="msg">Сообщение</label>
  <textarea id="msg" name="message" required></textarea>
  <button>Отправить</button>
  <p id="status" role="status"></p>
</form>
const form = document.querySelector('#feedback');
const status = document.querySelector('#status');

form.addEventListener('submit', async (event) => {
  event.preventDefault();
  const res = await fetch(form.action, { method: 'POST', body: new FormData(form) });
  status.textContent = res.ok ? 'Спасибо! Сообщение отправлено.' : 'Не удалось отправить, попробуйте ещё раз.';
  if (res.ok) form.reset();
});

Встроенная проверка required срабатывает до события submit, поэтому пустая форма не отправится. Адрес берётся из атрибута action — HTML остаётся рабочим и без JavaScript.

FormData: данные формы

Методы FormData
МетодЧто делает
get(name)значение поля
getAll(name)все значения: для чекбоксов с одним name
set(name, value)задать значение
append(name, value)добавить ещё одно значение
has(name), delete(name)проверить, удалить
const data = new FormData(form);
data.get('name');           // 'Аня'
data.getAll('tech');        // ['html', 'css'] — отмеченные чекбоксы
const obj = Object.fromEntries(data);   // { name: 'Аня', message: '…', tech: 'css' }

Object.fromEntries для полей с несколькими значениями оставит только последнее. Для чекбоксов используй getAll.

Доступ к полям: form.elements

form.elements.email.value;     // поле name="email"
form.email.value;              // короче, но может конфликтовать со свойствами формы
form.elements.agree.checked;   // чекбокс
form.elements.plan.value;      // выбранная радиокнопка из группы name="plan"

form.elements — коллекция полей по name. Для группы радиокнопок value возвращает значение выбранной. Поле с name="submit" или name="action" перекроет одноимённые свойства формы — таких имён лучше избегать.

Отправка JSON

const body = JSON.stringify(Object.fromEntries(new FormData(form)));
await fetch('/api/order', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body,
});

FormData уходит в формате multipart/form-data — так можно отправлять и файлы. Если сервер ждёт JSON, преобразуй данные и укажи заголовок Content-Type. Файлы в JSON так не передать.

Состояние отправки

form.addEventListener('submit', async (e) => {
  e.preventDefault();
  const btn = form.querySelector('button[type="submit"], button:not([type])');
  btn.disabled = true;
  btn.textContent = 'Отправляем…';
  try {
    const res = await fetch(form.action, { method: 'POST', body: new FormData(form) });
    if (!res.ok) throw new Error(res.status);
    status.textContent = 'Готово!';
    form.reset();
  } catch {
    status.textContent = 'Ошибка сети. Попробуйте ещё раз.';
  } finally {
    btn.disabled = false;
    btn.textContent = 'Отправить';
  }
});

Блокировка кнопки защищает от двойной отправки, текст «Отправляем…» показывает, что что-то происходит. finally возвращает кнопку в исходное состояние при любом результате.

Проверка перед отправкой

form.addEventListener('submit', (e) => {
  const phone = form.elements.phone;
  if (!/^\+?\d[\d\s()-]{9,}$/.test(phone.value)) {
    e.preventDefault();
    phone.setCustomValidity('Введите телефон, например +7 900 123-45-67');
    phone.reportValidity();
  }
});
form.elements.phone.addEventListener('input', (e) => e.target.setCustomValidity(''));

setCustomValidity задаёт своё сообщение для встроенной проверки, reportValidity показывает его. Пустая строка снимает ошибку. Подробнее — проверка формы.

Какая кнопка нажата

<button name="intent" value="draft">Сохранить черновик</button>
<button name="intent" value="publish">Опубликовать</button>
form.addEventListener('submit', (e) => {
  e.preventDefault();
  const data = new FormData(form, e.submitter);   // включит intent нажатой кнопки
  console.log(data.get('intent'));               // 'draft' или 'publish'
});

event.submitter — кнопка, которой отправили форму. Второй аргумент FormData добавляет её name и value в данные.

submit или click

submit у формы

Срабатывает и по кнопке, и по Enter в поле, после встроенной проверки.

click у кнопки

Пропускает отправку по Enter и встроенную проверку полей.

Частые ошибки

  • Слушают click кнопки вместо submit формы.
  • Забывают preventDefault, и страница перезагружается.
  • Не задают полям name, и FormData пустой.
  • Теряют значения чекбоксов при Object.fromEntries.
  • Не блокируют кнопку и получают двойную отправку.

Доступность

Результат отправки выводи в элемент с role="status" или aria-live, чтобы скринридер его объявил. При ошибках ставь фокус на первое неверное поле и связывай сообщение через aria-describedby. Не убирай встроенную проверку без замены.

Поддержка браузерами

FormData, form.elements, event.submitter и Object.fromEntries поддерживаются всеми актуальными браузерами. Второй аргумент FormData (submitter) — в актуальных версиях.

Практика

Сделай форму подписки (email и чекбоксы тем «css», «js»). По отправке: собери email и массив выбранных тем, отправь JSON на /api/subscribe, на время отправки заблокируй кнопку, выведи результат в элемент со статусом.

Показать решение
const form = document.querySelector('#subscribe');
const status = form.querySelector('[role="status"]');

form.addEventListener('submit', async (e) => {
  e.preventDefault();
  const data = new FormData(form);
  const payload = { email: data.get('email'), topics: data.getAll('topic') };
  const btn = e.submitter;
  btn.disabled = true;
  try {
    const res = await fetch('/api/subscribe', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    });
    status.textContent = res.ok ? 'Подписка оформлена' : 'Ошибка, попробуйте позже';
  } finally {
    btn.disabled = false;
  }
});

Открыть песочницу

Частые вопросы

Как получить данные формы в JavaScript?

new FormData(form), а для объекта — Object.fromEntries(formData).

Как отправить форму без перезагрузки?

В обработчике submit вызови preventDefault и отправь данные через fetch.

Почему FormData пустой?

У полей нет атрибута name.

Как получить все отмеченные чекбоксы?

formData.getAll('name').

Как узнать, какой кнопкой отправили форму?

Через event.submitter.

Источники

Ещё в разделе JavaScript