CodeMIRAI
0 XP

Built-in validation

2 min read 3 runnable examples Quiz included

Browsers can check form input before it's sent, with no JavaScript at all, using a few attributes.

HTML
<form id="signup">
  <label for="email">Email (required)</label><br>
  <input id="email" name="email" type="email" required><br><br>
  <button>Sign up</button>
</form>
<p id="result"></p>

<script>
  // a real site would send the form now; here we just report it
  document.querySelector("#signup").addEventListener("submit", (e) => {
    e.preventDefault();
    document.querySelector("#result").textContent = "Valid! The form would be sent now.";
  });
</script>

Press Sign up with the field empty, then with asha@ in it, then with a full address. The browser blocks the first two and explains why. The submit event only fires once every field is valid.

The validation attributes

AttributeRuleExample
requiredmust not be empty<input required>
typemust look like an email, URL or numbertype="email"
minlength, maxlengthlimits on text lengthminlength="8"
min, maxlimits on numbers and datesmin="13" max="120"
stepallowed incrementsstep="0.5"
patternmust match a regular expressionpattern="[A-Z]{3}[0-9]{3}"

A complete example

HTML
<style>
  body { font-family: system-ui; }
  label { display: block; margin-top: 10px; }
  input:invalid:not(:placeholder-shown) { border: 2px solid crimson; }
  input:valid:not(:placeholder-shown)   { border: 2px solid seagreen; }
</style>
<form id="f">
  <label for="user">Username: 3 to 15 letters or numbers</label>
  <input id="user" name="user" required minlength="3" maxlength="15"
         pattern="[A-Za-z0-9]+" placeholder="asha21">

  <label for="age">Age</label>
  <input id="age" name="age" type="number" min="13" max="120" required placeholder="21">

  <label for="code">Course code (optional), like PYT101</label>
  <input id="code" name="code" pattern="[A-Z]{3}[0-9]{3}" placeholder="PYT101"
         title="Three capital letters, then three digits">

  <p><button>Register</button></p>
</form>
<p id="ok"></p>
<script>
  document.querySelector("#f").addEventListener("submit", (e) => {
    e.preventDefault();
    document.querySelector("#ok").textContent = "All fields are valid.";
  });
</script>
  • Borders turn red or green as you type, thanks to the :invalid and :valid CSS pseudo-classes. Adding :not(:placeholder-shown) keeps untouched, empty fields from turning red before anyone has typed.
  • pattern must match the whole value. The title appears in the error message, so describe the format in words there.
  • An empty optional field is valid: pattern only checks a value that's there.

Custom messages

JavaScript can replace the browser's message with your own, for rules HTML can't express, like "the passwords must match":

HTML
<form id="pw">
  <input id="p1" type="password" placeholder="Password (8+)" required minlength="8">
  <input id="p2" type="password" placeholder="Repeat password" required>
  <button>Save</button>
</form>
<p id="done"></p>
<script>
  const p1 = document.querySelector("#p1");
  const p2 = document.querySelector("#p2");
  function checkMatch() {
    p2.setCustomValidity(p2.value === p1.value ? "" : "The passwords don't match.");
  }
  p1.addEventListener("input", checkMatch);
  p2.addEventListener("input", checkMatch);
  document.querySelector("#pw").addEventListener("submit", (e) => {
    e.preventDefault();
    document.querySelector("#done").textContent = "Saved.";
  });
</script>

setCustomValidity("message") marks a field invalid with your message, and an empty string marks it valid again.

Turning it off

<form novalidate> skips the browser's checks. Sites do this when JavaScript performs its own validation and shows errors in its own style.

Finished this lesson?Mark it complete to earn +20 XP and move your progress on.