Skip to main content

Forms Worth Trusting

intermediate14 min readLesson 67 of 143

Constraint validation, custom validators, inline error messages with aria-invalid and aria-describedby, and dynamic field sets.

Beginner forms have a submit handler. Intermediate forms earn trust: they validate like the server will, explain themselves inline, and stay accessible while doing it.

Constraint validation API

The browser already validates โ€” read its verdict instead of duplicating rules:

const email = form.elements.email;
if (!email.validity.valid) {
  // email.validity.valueMissing, .typeMismatch, .tooShort ...
  email.setCustomValidity("Enter an email like ada@example.com");
} else {
  email.setCustomValidity("");
}

setCustomValidity("") clears a custom error; any other string marks the field invalid. form.reportValidity() shows the message the way the platform intends. Use built-in attributes first โ€” required, type="email", minlength, pattern โ€” then custom code for cross-field rules like password confirmation.

Inline errors that assistive tech can see

A red border is decoration; the accessible signal is programmatic:

field.setAttribute("aria-invalid", "true");
errorBox.textContent = "Age must be a number";
field.setAttribute("aria-describedby", "age-error");

The error box needs id="age-error" and role="alert" so screen readers announce it when it appears. Clear all three when the field becomes valid.

Dynamic field sets

Adding rows (tasks, guests, line items) is delegation again: one listener on the container, closest("[data-row]") to find the row to remove, and names that stay unique โ€” either indexed (task-0, task-1) or irrelevant because you serialize from the DOM on submit.

Now practice

Advanced Forms โ€” PracticeValidate like the server will: field validators, password-match rules, and a row manager for dynamic field sets.3 challenges ยท ยท ~20 min