SKU // 136

JS Validation

Forms are where people type things wrong. The browser can check many rules for you (a required field, an email shape, a number in range) and JavaScript can read the results through the Constraint Validation API. This lesson covers the HTML rules, the validity object that reports each problem, custom messages, and how to show errors your own way.

// Rules in HTML

You set rules with attributes on form fields. The browser checks them when the form is submitted and stops with a message if a rule is broken:

Attribute Rule
requiredthe field cannot be empty
type="email"must look like an email address (also url, number)
minlength / maxlengthleast and most characters
min / maxsmallest and largest number or date
stepa number must be a multiple of this (from min)
patternmust match a regular expression
<form>
  <input name="code" required pattern="[A-Z]{3}-[0-9]{4}" placeholder="ABC-1234">
  <input name="age" type="number" min="13" max="120">
  <button>Send</button>
</form>

// The validity Object

Every field has a validity object. Each property is true when that specific rule is broken, and validity.valid is true only when nothing is wrong:

Property True when
valueMissinga required field is empty
typeMismatchthe value does not fit the type (an email without an @)
patternMismatchthe value does not match pattern
rangeUnderflow / rangeOverflowa number is below min or above max
tooShort / tooLongtext is under minlength or over maxlength (only after the person edits it)
stepMismatcha number does not fit the step
customErroryour own code marked it invalid
validno problems at all

You can create a field in JavaScript and test the rules without a page. checkValidity() returns true or false:

const input = document.createElement("input");
input.required = true;

console.log(input.validity.valueMissing, input.checkValidity());

input.value = "Ava";
console.log(input.validity.valueMissing, input.checkValidity());
true false false true
const input = document.createElement("input");
input.type = "email";

input.value = "ava";
console.log(input.validity.typeMismatch);

input.value = "ava@school.org";
console.log(input.validity.typeMismatch, input.validity.valid);
true false true
const input = document.createElement("input");
input.type = "number";
input.min = 1;
input.max = 10;

input.value = "0";
console.log(input.validity.rangeUnderflow, input.validity.rangeOverflow);
input.value = "11";
console.log(input.validity.rangeUnderflow, input.validity.rangeOverflow);
input.value = "5";
console.log(input.validity.valid);
true false false true true
const input = document.createElement("input");
input.pattern = "[A-Z]{3}-[0-9]{4}";

for (const value of ["ABC-1234", "abc-1234", "AB-12"]) {
  input.value = value;
  console.log(value, input.validity.patternMismatch);
}
ABC-1234 false abc-1234 true AB-12 true
Some rules only count typed text
minlength and maxlength are checked only when the person has edited the field, not when your code sets its value. That is why tooShort is not shown in these examples. It is a quirk of the browser, not a bug in your code.

// Your Own Rules: setCustomValidity

Some rules HTML cannot express: "the two passwords must match". input.setCustomValidity(message) marks a field invalid with your own message. Clearing it with an empty string is essential: a field stays invalid until you do:

const input = document.createElement("input");
input.value = "abc";

input.setCustomValidity("Please use at least 5 letters");
console.log(input.validity.customError, input.checkValidity());
console.log(input.validationMessage);

input.setCustomValidity("");
console.log(input.validity.customError, input.checkValidity());
true false Please use at least 5 letters false true

// Checking a Whole Form

form.checkValidity() is true only if every field passes. form.reportValidity() does the same and also shows the browser's message on the first bad field. Inside a submit handler, stop a bad form with event.preventDefault():

form.addEventListener("submit", function (event) {
  if (!form.checkValidity()) {
    event.preventDefault();
    console.log("Fix the form first");
  }
});

// Showing Errors Your Own Way

The built-in tooltips are plain. Add novalidate to the form to switch them off, and write each field's validationMessage into the page yourself. This form checks as you click Send and also checks that the two passwords match:

<form id="f" novalidate>
  <p><input id="email" type="email" required placeholder="Email"> <span id="e1" style="color:crimson"></span></p>
  <p><input id="pw" type="password" required placeholder="Password"></p>
  <p><input id="pw2" type="password" required placeholder="Repeat password"> <span id="e2" style="color:crimson"></span></p>
  <button>Sign up</button>
  <p id="done"></p>
</form>

<script>
  const form = document.getElementById("f");
  const email = document.getElementById("email");
  const pw = document.getElementById("pw");
  const pw2 = document.getElementById("pw2");

  form.addEventListener("submit", function (event) {
    event.preventDefault();
    pw2.setCustomValidity(pw.value === pw2.value ? "" : "Passwords do not match");
    document.getElementById("e1").textContent = email.validationMessage;
    document.getElementById("e2").textContent = pw2.validationMessage;
    document.getElementById("done").textContent = form.checkValidity() ? "Looks good!" : "";
  });
</script>

// Styling With CSS

CSS can react to the same rules. :invalid and :valid match fields by their state. A field that is empty and required counts as invalid from the start, which looks harsh, so many sites use :user-invalid, which applies only after the person has interacted with the field:

input:user-invalid { border-color: crimson; }
input:valid        { border-color: seagreen; }
Validation in the browser is not security
Everything here can be switched off or bypassed by anyone, because it runs on their computer. Treat it as a convenience that gives quick feedback. A server must always check the data again before using it.

// Quick Check

Which property is true when a required field is left empty?

  1. validity.typeMismatch
  2. validity.valueMissing
  3. validity.customError
  4. validity.tooShort
Show answer
B. valueMissing flags an empty required field.

Why must you call setCustomValidity("") once the problem is fixed?

Show answer
A field with a custom message stays invalid until you clear it. Passing an empty string removes the custom error so the field can become valid again.

What does this log?

const input = document.createElement("input");
input.type = "number";
input.max = 10;
input.value = "11";
console.log(input.validity.rangeOverflow);
Show answer
true. 11 is above the max of 10.

// Try It

  1. Click Open Playground and try the form with good and bad values
  2. Add a pattern that requires the username to start with a letter
  3. Add an age field with min 13 and show a friendly message when it fails
  4. Explain in your notes why the server must check the data again
// Video Walkthroughs
Last updated: Oct 11, 2026, 8:57 PM ET