Built-in validation
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
| Attribute | Rule | Example |
|---|---|---|
required | must not be empty | <input required> |
type | must look like an email, URL or number | type="email" |
minlength, maxlength | limits on text length | minlength="8" |
min, max | limits on numbers and dates | min="13" max="120" |
step | allowed increments | step="0.5" |
pattern | must match a regular expression | pattern="[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
:invalidand:validCSS pseudo-classes. Adding:not(:placeholder-shown)keeps untouched, empty fields from turning red before anyone has typed. patternmust match the whole value. Thetitleappears in the error message, so describe the format in words there.- An empty optional field is valid:
patternonly 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.