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:
| Control | Read 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 group | Find 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:
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:
<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:
field.validity.valid— is it valid? Specific flags includevalueMissing,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
novalidateattribute turns off automatic blocking so your code decides when to show messages.
Showing errors as the user types
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.
Sending form data
To submit without reloading, send the data with fetch (lesson 49):
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.