Chapter 27: Form Validation & Constraint Validation API

The Constraint Validation API

Programmatic Form Auditing: Mastering `checkValidity()`, `reportValidity()`, and All 10 `ValidityState` Flags

LEARNING OBJECTIVES
  • Master the complete Constraint Validation API methods: checkValidity(), reportValidity(), and setCustomValidity().
  • Deconstruct all 10 individual boolean flags of the ValidityState interface.
  • Understand the difference between silent validity evaluation (checkValidity()) and UI-rendering evaluation (reportValidity()).
  • Architect a scalable, production-grade error message dispatcher that translates low-level ValidityState flags into localized UI strings.
  • Batch-audit entire forms via HTMLFormElement.elements and form.checkValidity().
🎬 INTERACTIVE VISUAL PIPELINE Core Architecture Simulation
🌐
1. Input
Directives & Tags
⚙️
2. Parse
Tokenizer & AST
🌳
3. Layout
Box Model & Flow
🎨
4. Render
GPU Paint & Composite
PHASE 1: INPUT & DIRECTIVES
Browser receives declarative markup stream, parsing tag tokens and initializing component state.

📖 The Mental Model & Story (Intuitive Foundation)

Imagine sitting in the cockpit of a commercial airliner performing a pre-flight diagnostic check:

+-----------------------------------------------------------------------------+
|                     AIRCRAFT AVIONICS DIAGNOSTIC CONSOLE                    |
+-----------------------------------------------------------------------------+
|                                                                             |
|   [ PILOT RUNS DIAGNOSTIC ] ──► checkValidity()                             |
|                                       │                                     |
|                                       ▼                                     |
|             ┌───────────────────────────────────────────────────┐           |
|             │           10-POINT SENSOR STATUS MATRIX           │           |
|             ├─────────────────────────┬─────────────────────────┤           |
|             │ [🟢] valueMissing       │ [🔴] rangeUnderflow     │           |
|             │ [🟢] typeMismatch       │ [🟢] rangeOverflow      │           |
|             │ [🟢] patternMismatch    │ [🟢] stepMismatch       │           |
|             │ [🟢] tooShort           │ [🟢] badInput           │           |
|             │ [🟢] tooLong            │ [🟢] customError        │           |
|             └─────────────────────────┴─────────────────────────┘           |
|                                       │                                     |
|                                       ▼                                     |
|   SUMMARY: validity.valid = false                                           |
|   ACTION: reportValidity() ──► Rings alarm & points directly to Fuel Gauge  |
|                                                                             |
+-----------------------------------------------------------------------------+

An aircraft computer doesn't just show a generic "System Broken" red light. It monitors 10 independent sensor channels: Fuel Level (rangeUnderflow), Oxygen Pressure (rangeOverflow), Navigation Format (patternMismatch), Emergency Beacon Missing (valueMissing), etc.

In HTML5, every form control contains a ValidityState object containing 10 discrete boolean flags. As an engineer, you can inspect these individual flags programmatically to diagnose the exact reason an input failed and display targeted, human-friendly guidance.


Technical Deep Dive & Specifications

2.1 The Complete 10-Flag ValidityState Interface

Under the WHATWG HTML Standard (§ 4.10.21.2), element.validity is a read-only ValidityState interface containing exactly 10 constraint flags and 1 summary flag:

+----------------------------------------------------------------------------------------------------+
|                                THE 10 VALIDITYSTATE FLAGS REFERENCE                                |
+----------------------------------------------------------------------------------------------------+

 FLAG                  TYPE       CORRESPONDING CONSTRAINT RULE
 ───────────────────────────────────────────────────────────────────────────────────────────────────
 1. valueMissing       boolean    Element is 'required' and value is empty.
 2. typeMismatch       boolean    Value violates syntax for type="email" or type="url".
 3. patternMismatch    boolean    Value does not match the 'pattern' regular expression.
 4. tooShort           boolean    Value length is non-zero but strictly less than 'minlength'.
 5. tooLong            boolean    Value length exceeds 'maxlength' (e.g. via DOM assignment).
 6. rangeUnderflow     boolean    Numerical/temporal value is strictly less than 'min'.
 7. rangeOverflow      boolean    Numerical/temporal value is strictly greater than 'max'.
 8. stepMismatch       boolean    Value does not fit the stepping interval '(val - base) % step'.
 9. badInput           boolean    Browser cannot parse input (e.g. typing letters into type="number").
 10. customError       boolean    setCustomValidity() was called with a non-empty error string.
 ───────────────────────────────────────────────────────────────────────────────────────────────────
 *. valid              boolean    SUMMARY: true if and only if ALL 10 flags above are FALSE.

2.2 Methods of the Constraint Validation API

Method Target Return Value Fires invalid Event? Displays Native UI Bubble?
element.checkValidity() Single Input boolean Yes (if invalid) No (Silent)
element.reportValidity() Single Input boolean Yes (if invalid) Yes (Focuses & renders bubble)
element.setCustomValidity(msg) Single Input void N/A Updates message & customError flag
form.checkValidity() Entire <form> boolean Yes (on all invalid inputs) No (Silent)
form.reportValidity() Entire <form> boolean Yes (on all invalid inputs) Yes (Focuses first invalid)
const form = document.querySelector('form');

// 1. Silent check (Great for enabling/disabling submit buttons or tabs)
if (form.checkValidity()) {
  console.log('All inputs in form are completely valid!');
}

// 2. Interactive check (Triggers native browser error bubble on first failing element)
if (!form.reportValidity()) {
  console.log('Submission blocked; browser focused first invalid control.');
}

2.3 The Universal Error Message Resolver Pattern

Instead of relying on browser default strings, senior engineers map ValidityState flags to application-specific dictionaries:

function resolveErrorMessage(input) {
  const v = input.validity;
  if (v.valid) return '';

  if (v.valueMissing) return `${input.name} is mandatory. Please provide a value.`;
  if (v.typeMismatch) return `Please enter a valid ${input.type} format.`;
  if (v.patternMismatch) return input.title || `Format does not match required pattern.`;
  if (v.tooShort) return `Must be at least ${input.minLength} characters (currently ${input.value.length}).`;
  if (v.tooLong) return `Cannot exceed ${input.maxLength} characters.`;
  if (v.rangeUnderflow) return `Value must be at least ${input.min}.`;
  if (v.rangeOverflow) return `Value cannot exceed ${input.max}.`;
  if (v.stepMismatch) return `Please select a valid interval step (${input.step}).`;
  if (v.badInput) return `Please enter a valid number or date.`;
  if (v.customError) return input.validationMessage;

  return 'Invalid input value.';
}

💻 Interactive Code Playground

Starter Code

The following enterprise diagnostic console allows you to interact with multiple controls and inspect all 10 ValidityState flags in a real-time matrix.

Line-by-Line Code Breakdown

  • Lines 131-135 (FLAGS array): Declares all 10 standard WHATWG ValidityState flags in the exact specification taxonomy.
  • Lines 137-160 (renderMatrix()): Queries input.validity[flag] dynamically to illuminate the real-time LED diagnostic board.
  • Lines 163-176 (resolveErrorMessage()): The enterprise error resolver function that translates raw boolean flags into user-friendly localized copy.
  • Lines 183-191 (form.checkValidity() vs form.reportValidity()): Demonstrates the difference between silent evaluation and interactive popup reporting.

Expected Browser Render Output


SYS: ACTIVE
HULL: 98%
CORE: STABLE
NET: ONLINE
HTML STARSHIP CODE TERMINAL playground.html
LIVE RENDER & DIAGNOSTICS CORE TEMP: 45°C
INSPECTING DOM: VALID
TAGS: SCANNING...
+------------------------------------+  +------------------------------------+
| Form Inspector                     |  | 10-Point ValidityState Matrix      |
| Select an input to stream flags... |  | Inspecting: [Email] (Value: "")    |
|                                    |  |                                    |
| 1. Email (required, type=email)    |  | [🔴 TRUE ] valueMissing            |
| [                                ] |  | [🟢 FALSE] typeMismatch            |
|                                    |  | [🟢 FALSE] patternMismatch         |
| 2. Code (pattern: [A-Z]{3}-\d{3})  |  | [🟢 FALSE] tooShort                |
| [                                ] |  | [🟢 FALSE] tooLong                 |
|                                    |  | [🟢 FALSE] rangeUnderflow          |
| [ form.reportValidity() ]          |  | [🟢 FALSE] rangeOverflow           |
| [ form.checkValidity()  ]          |  | [⛔ FALSE] SUMMARY: validity.valid |
+------------------------------------+  +------------------------------------+

🏋️ Hands-On Exercise

🎯 The Challenge: Build an Accessible Form Audit Engine

Scenario: You are building an enterprise forms library. When a user submits an invalid form, instead of showing native browser bubbles, your engine must:

  1. Prevent default submission.
  2. Iterate through all submittable elements in form.elements.
  3. Collect all invalid inputs using input.validity.valid === false.
  4. Generate an accessible error summary list at the top of the form with anchor links focusing each invalid field.

Instructions:

  1. Create a form with 3 diverse inputs (Name: required, Age: min="18" max="99", Email: type="email" required).
  2. Add a container <div id="errorSummary" role="alert" tabIndex="-1"></div> at the top of the form.
  3. On submit, check form.checkValidity(). If false, build a <ul> of error messages inside #errorSummary with clickable links that .focus() the corresponding input.

🏁 Starter Code Sandbox

SYS: ACTIVE
HULL: 98%
CORE: STABLE
NET: ONLINE
STARTER CODE SANDBOX exercise.html
LIVE RENDER & DIAGNOSTICS CORE TEMP: 45°C
INSPECTING DOM: VALID
TAGS: SCANNING...

⚠️ Common Pitfalls

  1. Confusing checkValidity() with reportValidity(): checkValidity() is silent and never renders visual bubbles. If you want the browser to highlight and scroll to the invalid field, use reportValidity().
  2. Attempting to Mutate ValidityState: Writing input.validity.valid = false. The validity object is strictly read-only. You must use setCustomValidity() to programmatically modify validity state.
  3. The badInput Gotcha: When a user types non-numeric characters (like 12abc) into <input type="number">, input.value is evaluated as "" (empty string) in JavaScript, but validity.badInput will be true. Always check validity.badInput before assuming the field is empty!

💡 Pro Tips

  1. Batch Validation via HTMLFormControlsCollection: Iterate form.elements with Array.from(form.elements) to run automated pre-flight security sweeps before serializing into FormData or JSON.
  2. Silent State Observers: Use input.checkValidity() inside input event listeners to dynamically toggle submit button disabled states without bothering the user with intrusive popups.

📌 Key Takeaways

  • 10 ValidityState Flags: Provide granular insight into the exact failure condition (valueMissing, typeMismatch, patternMismatch, tooShort, tooLong, rangeUnderflow, rangeOverflow, stepMismatch, badInput, customError).
  • validity.valid: True if and only if all 10 individual flags are false.
  • checkValidity() vs reportValidity(): checkValidity() performs silent evaluation; reportValidity() performs evaluation AND renders the native error tooltip.
  • badInput Flag: Catches unparseable numerical and date entries where input.value returns empty string.
  • Form Batch Auditing: form.checkValidity() checks all candidate elements (willValidate === true) in DOM tree order.
  • --
⭐ LEARN: HTML 🌟 ⚔️ QUIZ BATTLE ARENA // ACTIVE
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 1 / 3

What is the difference between calling form.checkValidity() and form.reportValidity()?

Question 1 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 2 / 3

If a user types "abcd" into an <input type="number">, what is the value of input.validity.badInput in modern browsers?

Question 2 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 3 / 3

Can you directly assign element.validity.valueMissing = false in JavaScript?

Question 3 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP