Checking a Form with JavaScript

Let the browser check a form with required, min and type, ask it in code with checkValidity(), add rules of your own with setCustomValidity(), and show each error next to its field where everyone, screen reader users included, can find it.

Journeyman · JavaScript · HTML · Updated

After this lesson you can

  • Use the browser's built-in checks (required, min, type) and checkValidity() to find the fields that aren't filled in right
  • Add a rule of your own with setCustomValidity() and show its message next to the field, tied to it with aria-describedby

Key takeaways

  • Attributes such as required, min and type="email" make the browser check a field, and it won't send the form until every check passes.
  • checkValidity() answers true or false for one field or a whole form, and the field's validity says which rule failed.
  • setCustomValidity("message") marks a field as wrong, in your words. Call setCustomValidity("") once it's fixed, or the field stays wrong.
  • Put each error message in the page next to its field, and point the field's aria-describedby at it, so a screen reader reads it too.
  • Checks in the page help people fill a form in. A server still checks again, because anyone can get around a page's JavaScript.

You press Sign up and the page says Please fill out this field, with an arrow pointing at the box you missed. That's a form checking itself, and it's work you'll do on every form you build. Without it, the Reading List fills up with books that have no title, and a pizza order arrives with no address. With it, people find out what to fix right away, next to the box, in plain words. This lesson shows the checks the browser does for you, how to ask about them in code, how to add rules of your own, and how to show a message that everyone can find.

The browser's built-in checks

A post office won't take a parcel with no address on it: it checks before it accepts. Checking what someone typed before you accept it is called validation. When it happens in the browser, before anything is sent anywhere, it's client-side validation (the browser is the client). In your textbook it may be called HTML5 form validation or constraint validation, since each rule is a constraint on what a field accepts.

You switch the checks on with attributes in the HTML:

<input id="title" name="title" type="text" required>
<input id="pages" name="pages" type="number" required min="1">
<input id="email" name="email" type="email">
  • required: the field can't be left empty.
  • min="1" (and max): a number box's lowest (and highest) value.
  • type="email": the text must look like an email address, with an @ and something after it.

Press Add book with the title empty and the browser stops the form being sent, puts the cursor in the title box, and shows a small bubble: in Chrome, Please fill out this field. No JavaScript needed.

Asking in code: checkValidity()

Your code can ask the same question. Every field has a checkValidity() method that answers true (it passes every check) or false. Its validity property says which check failed, and validationMessage holds the browser's words:

const pagesInput = document.getElementById("pages");
pagesInput.value = "0";

console.log(pagesInput.checkValidity());
console.log(pagesInput.validity.rangeUnderflow);
console.log(pagesInput.validationMessage);

With min="1", the Console shows false, true, and in Chrome Value must be greater than or equal to 1. The validity property holds a true or false for each rule:

validity property It's true when…
valueMissing a required field is empty
rangeUnderflow a number is below min
rangeOverflow a number is above max
typeMismatch the text doesn't fit the type, such as an email with no @
customError your own rule failed (next section)

A form has checkValidity() too, and it answers true only when every field in it passes: form.checkValidity().

Try it. In your reading-list page, add required to the title box and required min="1" to the pages box. Press Add book with the title empty, then with 0 pages. Then type document.getElementById("pages").validity in the Console and open it up to see every rule.

Your own rule: setCustomValidity()

The built-in checks miss things. Type three spaces in the title box and required is happy: a space counts as something. But a book called " " isn't a book. For a rule like that, setCustomValidity() marks a field as wrong, with your message:

const titleInput = document.getElementById("title");

titleInput.addEventListener("input", () => {
  if (titleInput.value.trim() === "") {
    titleInput.setCustomValidity("A title needs at least one letter.");
  } else {
    titleInput.setCustomValidity("");
  }
});
  • setCustomValidity("A title needs at least one letter.") makes checkValidity() answer false, sets validity.customError to true, and the browser shows your words in its bubble.
  • setCustomValidity(""), with an empty string, says "this field is fine now". Forgetting it is the most common bug here: the field stays wrong forever, however the visitor fixes it, and the form can never be sent.

The listener is for input, which Form Events showed fires with every change to the box, so the rule is checked again as the visitor types.

A message everyone can find

The browser's bubble disappears after a few seconds, and looks different in every browser. A message that stays is better: like a sticky note on the exact box of a paper form that needs fixing. Put an empty paragraph right after the field, for your code to fill in:

<form id="add-book" novalidate>
  <label for="title">Title</label>
  <input id="title" name="title" type="text" required aria-describedby="title-error">
  <p id="title-error" class="error"></p>
  <button type="submit">Add book</button>
</form>
  • aria-describedby="title-error" ties the message to the field. ARIA (Accessible Rich Internet Applications) is a set of attributes that tell screen readers what a page can't show them; aria-describedby names the element that describes this field. When someone using a screen reader moves to the title box, it reads the label, then the message.
  • novalidate on the form switches off the browser's bubbles, because your messages replace them. The checks still work: checkValidity() answers exactly as before.

An error shown only as a red border fails people who can't see red, so the message is always in words.

Putting it together when the form is sent

The best moment to check everything is when the form is sent. Listen for submit, cancel the sending with event.preventDefault() as Canceling Events showed (the Reading List keeps its books in the page, so it never sends the form anywhere), then check:

const form = document.getElementById("add-book");
const titleInput = document.getElementById("title");
const titleError = document.getElementById("title-error");

form.addEventListener("submit", (event) => {
  event.preventDefault();
  if (!titleInput.checkValidity()) {
    titleError.textContent = "Every book needs a title.";
    titleInput.setAttribute("aria-invalid", "true");
    titleInput.focus();
    return;
  }
  titleError.textContent = "";
  titleInput.removeAttribute("aria-invalid");
  console.log(`Added: ${titleInput.value.trim()}`);
});

When the title is missing, the message appears under the box, aria-invalid="true" tells a screen reader the field has a problem, and focus() puts the cursor in the box, ready to fix. When it's fixed, the message and the mark are cleared. With a title typed, the Console shows Added: The Hobbit.

One thing these checks are not: security. Anyone can switch off a page's JavaScript, or send data to a server without using your page at all. Checks in the page are there to help people; a site that stores the data checks it again on its server (server-side validation).

Check yourself

2 questions to try on the lesson page.

So what

A form that says exactly what's wrong, right where it's wrong, gets filled in faster and by more people, including the ones using a keyboard or a screen reader. The same three moves work on any form: let the browser check what it can, add your own rules, and put the message next to the field.

Key terms

Cheat sheets

Related