JavaScript Forms and Validation
Forms collect information from users. JavaScript reads that information, checks it, and gives quick feedback before anything is sent.
Reading Form Values
Each input has a value property. For checkboxes, use checked. A select box's value is the chosen option's value.
Example
<!DOCTYPE html>
<html>
<body>
<form id="order">
<input name="customer" placeholder="Your name"><br><br>
<select name="size">
<option value="S">Small</option>
<option value="M" selected>Medium</option>
<option value="L">Large</option>
</select>
<label><input type="checkbox" name="gift"> Gift wrap</label><br><br>
<button>Place order</button>
</form>
<p id="msg"></p>
<script>
const form = document.getElementById("order");
form.addEventListener("submit", (event) => {
event.preventDefault();
const name = form.elements.customer.value;
const size = form.elements.size.value;
const gift = form.elements.gift.checked;
document.getElementById("msg").textContent =
`Thank you ${name}! Size: ${size}. Gift wrap: ${gift ? "yes" : "no"}.`;
});
</script>
</body>
</html>Try it Yourself »form.elements.customer finds the field with name="customer". The name attribute is the key to everything in forms.
FormData: All Fields at Once
new FormData(form) collects every named field. Object.fromEntries() turns it into a normal object, ready to send as JSON:
Example
form.addEventListener("submit", async (event) => {
event.preventDefault();
const data = Object.fromEntries(new FormData(form));
console.log(data); // { customer: "Ali", size: "M", gift: "on" }
await fetch("/api/orders", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
});An unchecked checkbox is simply missing from FormData, and a checked one has the value "on" (unless you give it a value).
Built-in HTML Validation
Before writing any JavaScript, use HTML attributes. The browser checks them for you when the form is submitted:
| Attribute | Checks |
|---|---|
required | The field is not empty |
type="email", type="url" | The text looks like an email or link |
minlength, maxlength | Length of the text |
min, max, step | Range for numbers and dates |
pattern="..." | The value matches a regular expression |
Example
<input name="phone" required pattern="03[0-9]{2}-?[0-9]{7}" title="Example: 0300-1234567">The Constraint Validation API
JavaScript can read the result of those checks:
| Property / Method | What it does |
|---|---|
input.checkValidity() | true if the field is valid |
input.validity | Details: valueMissing, typeMismatch, tooShort, patternMismatch, rangeOverflow... |
input.validationMessage | The browser's error text |
input.setCustomValidity("msg") | Marks the field invalid with your message. Pass "" to make it valid again |
form.reportValidity() | Checks all fields and shows the messages |
Custom Messages and Live Feedback
This sign-up form uses HTML rules plus one custom rule (passwords must match), shows friendly messages under each field, and checks while the user types.
Example
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: sans-serif; padding: 16px; }
label { display: block; margin-top: 12px; }
input { padding: 6px; width: 240px; }
.error { color: #c0392b; font-size: 13px; min-height: 16px; }
input.invalid { border: 2px solid #c0392b; }
</style>
</head>
<body>
<form id="signup" novalidate>
<label>Email <input name="email" type="email" required></label>
<div class="error" id="email-error"></div>
<label>Password <input name="password" type="password" required minlength="8"></label>
<div class="error" id="password-error"></div>
<label>Confirm password <input name="confirm" type="password" required></label>
<div class="error" id="confirm-error"></div>
<br><button>Create account</button>
</form>
<p id="done"></p>
<script>
const form = document.getElementById("signup");
function messageFor(input) {
const v = input.validity;
if (v.valueMissing) return "This field is required.";
if (v.typeMismatch) return "Please enter a valid email, like ali@example.com.";
if (v.tooShort) return "Use at least " + input.minLength + " characters.";
if (v.customError) return input.validationMessage;
return "";
}
function checkField(input) {
if (input.name === "confirm") {
const same = input.value === form.elements.password.value;
input.setCustomValidity(same ? "" : "Passwords do not match.");
}
const msg = messageFor(input);
document.getElementById(input.name + "-error").textContent = msg;
input.classList.toggle("invalid", msg !== "");
return msg === "";
}
form.addEventListener("input", (event) => {
checkField(event.target);
if (event.target.name === "password") checkField(form.elements.confirm);
});
form.addEventListener("submit", (event) => {
event.preventDefault();
const fields = [...form.elements].filter(el => el.name);
const allValid = fields.map(checkField).every(Boolean);
document.getElementById("done").textContent = allValid ? "Account created!" : "";
});
</script>
</body>
</html>Try it Yourself »Some points to notice:
novalidateon the form turns off the browser's own pop-ups, so we can show our own messages. The rules still work throughvalidity.- One
inputlistener on the form handles every field, using event delegation. fields.map(checkField)runs every check first, so all errors show at once, not only the first.
Front-end validation is for a better user experience only. Anyone can turn off JavaScript or send data directly to your server, so always validate again on the server.
Styling With CSS
CSS can react to validity too. :user-invalid matches a field only after the user has interacted with it, so empty forms do not start out red:
Example
input:user-invalid { border-color: crimson; }
input:valid { border-color: seagreen; }Exercise
Exercise
Which method lets you mark a field as invalid with your own error message?
