Browser validation

The Constraint Validation API

Read validity states, set a custom message, and trigger or report browser validation without rebuilding the browser’s validation system.

The browser already validates required, minlength, pattern and the other attributes we saw. The Constraint Validation API is the JavaScript side of that same system. It lets you read the result, add your own rules, and decide when the browser shows its messages.

Read the state of a control

Every form control has a validity object. It’s a set of booleans, one per kind of failure.

Take an empty required email field. Ask it what’s wrong:

const email = document.querySelector('#email')

email.validity.valid //false
email.validity.valueMissing //true
email.validity.typeMismatch //false

Type ada@ and valueMissing becomes false while typeMismatch turns true. Other flags cover tooShort, tooLong, rangeUnderflow, rangeOverflow and patternMismatch. You never write a regular expression to know why a field failed. The browser tells you.

Check or report

There are two ways to run the check from code. checkValidity() returns true or false and stays silent. reportValidity() returns the same value and also shows the browser’s message bubble on the first invalid field:

form.checkValidity() //false, nothing visible
form.reportValidity() //false, the browser points at the empty field

I use checkValidity() when I want to control the presentation myself. I use reportValidity() when the native bubbles are good enough.

Add a rule HTML can’t express

Some rules involve two fields. A password confirmation is the classic case. No attribute can say “must equal that other input”, so we write it:

const form = document.querySelector('form')
const password = form.elements.password
const confirmation = form.elements.confirmation

function validateConfirmation() {
  const matches = confirmation.value === password.value
  confirmation.setCustomValidity(matches ? '' : 'Passwords do not match')
}

password.addEventListener('input', validateConfirmation)
confirmation.addEventListener('input', validateConfirmation)

setCustomValidity() with a non-empty string marks the control invalid and stores the message. Now confirmation.validity.customError is true, checkValidity() fails, and native submission stops.

Notice the empty string in the matches case. That’s what clears the custom error. Forget it and the field stays invalid forever, even after the person fixes it. This is the bug I see most often with this API.

Where JavaScript fits

Keep the declarative attributes for what HTML understands: required, min, max, pattern. Reach for JavaScript for relationships between fields, or for a nicer way to show the messages. Don’t rebuild the whole validation system in a script. The browser already did that work.

And this API still runs on the visitor’s machine. The server repeats every rule before it changes any data.

Try this: type two different passwords, call form.checkValidity() in the console, then make them match and call it again. You should see false and then true.

Lesson completed