Forms & Validation

Forms are how users send information: sign-ups, searches, checkouts, settings. JavaScript lets you read form values, validate them with helpful messages before anything is submitted, and send the data without reloading the page.

Reading values

Each control exposes its current value through a property:

ControlRead with
Text, email, number, textarea, select.value (always a string)
Number and range inputs.valueAsNumber for a real number
Date inputs.value ("2026-10-06") or .valueAsDate
Checkbox.checked (true/false)
Radio groupFind the checked one: form.querySelector('[name=size]:checked')
Multi-select[...select.selectedOptions].map(o => o.value)

The submit event and FormData

Listen for submit on the form, not click on the button — that way pressing Enter works too. FormData collects every named field at once:

Try it yourself
Result

Only fields with a name attribute are included. Object.fromEntries(formData) turns it into a plain object; use getAll for fields with several values, like checkbox groups.

Built-in HTML validation

HTML attributes give you validation for free — the browser blocks submission and shows a message:

HTML form.html
<input type="email" required>
<input type="number" min="1" max="10">
<input minlength="8" maxlength="64">
<input pattern="[A-Z]{3}-\d{3}" title="Format: ABC-123">

Always use these first: they're fast, accessible and work without JavaScript. JavaScript adds custom rules and nicer messages on top.

The Constraint Validation API

Every field has a validity object and methods for custom messages:

Try it yourself
Result
  • field.validity.valid — is it valid? Specific flags include valueMissing, typeMismatch, tooShort, patternMismatch, rangeOverflow.
  • field.setCustomValidity(message) — mark it invalid with your own message; pass "" to clear.
  • form.checkValidity() — true if every field is valid; reportValidity() also shows the messages.
  • The novalidate attribute turns off automatic blocking so your code decides when to show messages.

Showing errors as the user types

Try it yourself
Result

A user-friendly pattern: validate when the user leaves a field (blur), then re-check on every keystroke once an error is showing, so it disappears as soon as it's fixed. aria-invalid and aria-describedby make the error available to screen readers.

Important: client-side validation is for user experience only. Anyone can bypass it, so the server must always validate the data again.

Sending form data

To submit without reloading, send the data with fetch (lesson 49):

JS submit.js
form.addEventListener("submit", async event => {
  event.preventDefault();
  const button = form.querySelector("button");
  button.disabled = true;
  try {
    const res = await fetch("/api/signup", { method: "POST", body: new FormData(form) });
    if (!res.ok) throw new Error(`Server said ${res.status}`);
    form.reset();
  } catch (err) {
    alert(`Could not sign up: ${err.message}`);
  } finally {
    button.disabled = false;
  }
});

Disabling the button while the request is in flight prevents double submissions.