Формы и 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.
События полей #
| Событие | Применение |
|---|---|
input | live-фильтр, пересчёт суммы |
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()— нет.