Frontend / HTML / 01_constraint_validation.md

Constraint Validation API and Senior Input Types

Updated 5 interview angles 4 min read source
On this page10
  1. What is the Constraint Validation API?
  2. What’s on ValidityState?
  3. checkValidity() vs reportValidity() vs setCustomValidity()?
  4. How do you build real-time validation without a library?
  5. Which input types matter and what do they give you?
  6. inputmode, pattern, autocomplete — what do they add?
  7. Gotchas
  8. Related
  9. Further reading
  10. Interview angle

Constraint Validation API and Senior Input Types

The browser validates forms natively via the Constraint Validation API: required, type, min/max, pattern, minlength, etc. produce a ValidityState per field, surface :valid/:invalid CSS, and block submission. You read/override it in JS with checkValidity(), reportValidity(), and setCustomValidity(). Senior input types (email, url, tel, date, color, file multiple, search) plus inputmode and autocomplete give correct keyboards, pickers, and autofill for free. Pair this with accessible error messaging — see Accessible Forms.

What is the Constraint Validation API?

The built-in form-validation system. Declarative attributes (required, pattern, min, max, minlength, step, type) define constraints; the browser computes a validity object on each control and prevents submit if invalid. You don’t need a library for basic validation.

html
<input type="email" required minlength="5" />

What’s on ValidityState?

Boolean flags explaining why a field is invalid: valueMissing (required + empty), typeMismatch (bad email/url), patternMismatch, tooLong/tooShort, rangeUnderflow/rangeOverflow, stepMismatch, badInput, and valid (all good). Branch on these to show specific messages:

js
if (input.validity.valueMissing) show("This field is required.");
else if (input.validity.typeMismatch) show("Enter a valid email.");

checkValidity() vs reportValidity() vs setCustomValidity()?

  • checkValidity() — returns true/false, fires an invalid event on failing fields, shows nothing.
  • reportValidity() — same check and displays the browser’s native error bubble.
  • setCustomValidity(msg) — sets a custom error (non-empty = invalid); set it back to "" to clear. Used for cross-field rules (password confirmation):
js
confirm.setCustomValidity(confirm.value !== pw.value ? "Passwords don't match" : "");

How do you build real-time validation without a library?

Validate on input/blur, suppress native bubbles with novalidate on the form, and render your own accessible messages:

js
form.setAttribute("novalidate", "");
field.addEventListener("blur", () => {
  field.setCustomValidity("");                 // reset
  if (!field.checkValidity()) {
    field.setAttribute("aria-invalid", "true"); // a11y
    errorEl.textContent = messageFor(field.validity);
  } else { field.removeAttribute("aria-invalid"); errorEl.textContent = ""; }
});

novalidate keeps native submit-blocking off so you control UX, while still using the validity engine. Wire aria-invalid + aria-describedby per Accessible Forms.

Which input types matter and what do they give you?

Type Benefit
email / url / tel type validation + the right mobile keyboard
number / range numeric input, min/max/step
date / time / datetime-local / month native pickers, locale-aware
color native color picker
search clear button, search semantics
file (+ multiple, accept, capture) file picker, type filter, camera on mobile
password masking + autofill/manager integration

inputmode, pattern, autocomplete — what do they add?

  • inputmode="numeric" / "decimal" / "tel" — picks the on-screen keyboard without changing the control type or validation (great for OTP/PIN where type="number" misbehaves).
  • pattern="[0-9]{4}" — a regex constraint feeding patternMismatch.
  • autocomplete="email" / "one-time-code" / "cc-number" — enables autofill and AT recognition (WCAG 1.3.5). Use standard tokens; don’t blanket autocomplete="off".

Gotchas

  • Client validation is UX, not security — always re-validate on the server; constraints are trivially bypassed.
  • type="number" quirks — strips leading zeros, allows e, scroll-changes the value; for codes/IDs use type="text" + inputmode + pattern.
  • Native error bubbles aren’t styleable and vary by browser — for branded/accessible errors, novalidate + custom messages.
  • setCustomValidity is sticky — forgetting to reset it to "" leaves the field permanently invalid.
  • Date input formatting is locale/browser-dependent — the displayed format differs from the submitted YYYY-MM-DD value; don’t parse the display.
  • :invalid styles on load can flash before interaction — scope with :user-invalid (newer) or only style after blur/submit.

Further reading

Interview angle 5

  • “How much form validation can you do without a library?” - most of it. Attributes like required, type, pattern and min/max produce a ValidityState per control, and you branch on its flags - valueMissing, typeMismatch, patternMismatch - to show a specific message rather than a generic one.
    • Follow-up: “How do you handle a rule that spans two fields?” - setCustomValidity, which is the escape hatch for anything the attributes cannot express, such as a password confirmation. Remember to reset it to "", because it is sticky and a stale message leaves the field permanently invalid.
  • “Why would you add novalidate and then still use the API?” - because the native error bubbles cannot be styled and differ between browsers, but the validity engine is still worth having. novalidate turns off the browser’s UI and its submit blocking, and you render your own messages with aria-invalid and aria-describedby so screen readers get them too.
  • “What do the newer input types actually buy you?” - the right on-screen keyboard, native pickers for dates and colours, and type validation for free. inputmode gives you the keyboard without changing the control type, which matters for codes and PINs where type="number" strips leading zeros and accepts e.
  • “Why does autocomplete matter beyond convenience?” - standard tokens let password managers and assistive technology identify the field’s purpose, which is a WCAG 1.3.5 requirement. A blanket autocomplete="off" breaks that and usually breaks the user’s password manager as well.
  • “Is any of this security?” - none of it. Every constraint is client-side and trivially bypassed with a direct request, so the server must re-validate everything. Client validation is a UX feature that happens to look like a gate.