Формы и submit

Формы и submit #

Форма группирует поля ввода и отправляет их на сервер через HTTP или обрабатывает локально в JavaScript. Событие submit — точка перехвата оформления заказа: можно проверить данные, показать ошибки и отправить fetch без перезагрузки вкладки.


submit и preventDefault #

const checkoutForm = document.querySelector('#checkout-form')

checkoutForm.addEventListener('submit', async (event) => {
  event.preventDefault()

  if (!checkoutForm.reportValidity()) return

  const payload = new FormData(checkoutForm)
  await placeOrder(payload)
})

Без preventDefault браузер выполнит нативную отправку: страница перезагрузится или уйдёт на action формы.

Программная отправка (снова вызовет submit):

checkoutForm.requestSubmit() // предпочтительно — запускает validation
// checkoutForm.submit() — обходит submit-handler и constraint validation

FormData #

const form = document.querySelector('#checkout-form')
const data = new FormData(form)

data.get('email') // первое поле name="email"
data.getAll('addon') // все чекбоксы с одинаковым name

const body = Object.fromEntries(data.entries())
// { email: "...", city: "...", addon: "gift-wrap" }

Добавление полей вручную:

data.set('cartId', sessionStorage.getItem('cartId'))
data.append('source', 'jsflow-dashboard')

Для fetch передайте FormData напрямую или сериализуйте в JSON на стороне API.


Элементы формы #

Коллекция form.elements — все контролы с name:

const form = document.querySelector('#checkout-form')

form.elements.email // поле name="email"
form.elements['delivery-speed'] // name с дефисом

Чтение значений:

const email = form.elements.email.value
const isGift = form.elements.giftMessage.checked
const speed = form.elements.delivery.value // выбранный radio

Состояние кнопки оплаты:

const payBtn = form.querySelector('[type="submit"]')
payBtn.disabled = true

try {
  await validateCart()
  payBtn.disabled = false
} catch {
  showCartError()
}

Встроенная validation #

HTML-атрибуты: required, minlength, pattern, type="email":

<input name="email" type="email" required autocomplete="email" />
<input name="phone" pattern="\\+7[0-9]{10}" title="Формат: +7XXXXXXXXXX" />
const input = form.elements.phone

input.addEventListener('input', () => {
  input.setCustomValidity('')
  if (!input.checkValidity()) {
    input.setCustomValidity('Укажите телефон в формате +7XXXXXXXXXX')
  }
})

reportValidity() показывает браузерные подсказки и возвращает false, если форма невалida.


События полей #

СобытиеПрименение
inputlive-фильтр, пересчёт суммы
changeвыбор доставки, смена тарифа
invalidкастомное сообщение до submit
resetсброс формы к начальным значениям

Пересчёт итога checkout при смене доставки:

form.elements.delivery.addEventListener('change', () => {
  updateCheckoutTotal(form)
})

Практика: многошаговый checkout #

const form = document.querySelector('#checkout-form')
let step = 1

form.addEventListener('submit', (event) => {
  event.preventDefault()

  const stepPanel = form.querySelector(`[data-step="${step}"]`)
  const fields = stepPanel.querySelectorAll('input, select, textarea')
  const stepValid = [...fields].every((el) => el.reportValidity())

  if (!stepValid) return

  if (step < 3) {
    step += 1
    showCheckoutStep(form, step)
    return
  }

  submitOrder(new FormData(form))
})

Каждый шаг валидируется отдельно; финальный submit собирает все поля формы.


Итог #

  • Перехватывайте submit и вызывайте preventDefault для SPA-потока.
  • FormData — удобный снимок именованных полей; form.elements — прямой доступ по name.
  • reportValidity / setCustomValidity — встроенная и кастомная проверка.
  • requestSubmit() запускает validation; голый submit() — нет.

См. также #