Topic 6: JavaScript Validation
📖 16 min read · 🎯 intermediate · 🧭 Prerequisites: div-page-design, form-submission-task
Why this matters
You've filled out a form online — typed your email, set a password, clicked Submit — and the page just accepted gibberish. A phone number in the email field. A one-letter password. No warning, no pushback. That feels wrong, and it is.
JavaScript validation is the code that checks user input before it goes anywhere. It's your first line of defence against typos, accidents, and incomplete data. In this lesson, you'll learn how to write that check yourself — so your forms actually guide the user instead of silently accepting whatever they type.
What You'll Learn
- Distinguish client-side validation from server-side validation and know when each applies
- Implement basic form validation in JavaScript to ensure required fields are not empty
- Use regular expressions to validate email format and phone number format
- Enforce password length and confirm-password matching in an advanced registration form
The Analogy
Think of JavaScript validation as the security check at an airport departure gate. Before your luggage ever reaches the aircraft (the server), a gate agent (the browser) inspects it against a checklist: Does it have a tag? Is it within the weight limit? Does the name on the ticket match the ID? Only bags that pass every check are loaded onto the plane. If something is wrong, the problem is caught right there at the gate — cheaply and quickly — rather than halfway across the ocean where fixing it is expensive and embarrassing. Client-side validation is that gate agent: fast, immediate, and working entirely on the ground before anything takes flight.
Chapter 1: The Importance of Validation
Validation is the process of ensuring that the data entered by users meets certain criteria before it is processed. This protects against errors, security issues, and data inconsistencies that would otherwise reach your database or business logic.
Validation falls into two fundamental categories:
- Client-Side Validation — performed in the browser before the data is sent to the server. It gives users instant feedback and reduces unnecessary network requests.
- Server-Side Validation — performed on the server after the data is sent. It is the authoritative check and cannot be bypassed by disabling JavaScript.
Both layers are necessary in production systems. Client-side validation improves user experience; server-side validation enforces security. Never rely on client-side validation alone.
sequenceDiagram
participant User
participant Browser
participant Server
User->>Browser: Fills out form and clicks Submit
Browser->>Browser: Client-side validation (JS)
alt Validation fails
Browser-->>User: Show inline error messages
else Validation passes
Browser->>Server: POST form data
Server->>Server: Server-side validation
alt Server validation fails
Server-->>Browser: Error response
Browser-->>User: Show server error
else All good
Server-->>Browser: Success response
Browser-->>User: Confirmation page
end
end
Chapter 2: Basic Form Validation
The class began with the most common scenario — a registration form that collects a name and an email address. The goals are simple: neither field can be empty, and the email must match a recognisable email pattern.
The HTML Form
The form uses onsubmit="return validateForm()". Returning false from validateForm() cancels the submission; returning true allows it to proceed. Each field is paired with an empty <span> whose job is to display error text in red.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Form Validation Example</title>
<style>
.error {
color: red;
}
</style>
</head>
<body>
<h1>User Registration</h1>
<form id="registrationForm" onsubmit="return validateForm()">
<label for="name">Name:</label>
<input type="text" id="name" name="name">
<span id="nameError" class="error"></span>
<br>
<label for="email">Email:</label>
<input type="text" id="email" name="email">
<span id="emailError" class="error"></span>
<br>
<button type="submit">Register</button>
</form>
<script src="validation.js"></script>
</body>
</html>
validation.js
The script clears any previously displayed errors first (so stale messages from the last attempt don't accumulate), then reads the field values, and evaluates each rule in sequence. A single boolean flag isValid tracks whether every rule passed.
function validateForm() {
// Clear previous error messages
document.getElementById("nameError").textContent = "";
document.getElementById("emailError").textContent = "";
// Get form values
let name = document.getElementById("name").value;
let email = document.getElementById("email").value;
let isValid = true;
// Validate name
if (name === "") {
document.getElementById("nameError").textContent = "Name is required";
isValid = false;
}
// Validate email
if (email === "") {
document.getElementById("emailError").textContent = "Email is required";
isValid = false;
} else if (!validateEmail(email)) {
document.getElementById("emailError").textContent = "Invalid email format";
isValid = false;
}
return isValid;
}
// Helper function to validate email format
function validateEmail(email) {
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailPattern.test(email);
}
How the email regex works:
| Segment | Meaning |
|---|---|
^ | Start of string |
[^\s@]+ | One or more chars that are not whitespace or @ (the local part) |
@ | The literal @ sign |
[^\s@]+ | One or more chars (the domain name) |
\. | A literal dot |
[^\s@]+$ | One or more chars after the dot (TLD), end of string |
The pattern /^[^\s@]+@[^\s@]+\.[^\s@]+$/ is intentionally simple and pragmatic — it catches obvious typos without false-rejecting valid edge-case addresses.
Chapter 3: Advanced Validation
With basic validation in hand, the trainer pushed the class further. "Real registration forms need more: minimum password length, password confirmation, and phone number format." She unveiled the full advanced form.
The HTML Form
This form adds password, confirmPassword, and phone fields, each with its own error span and external script advancedValidation.js.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Advanced Form Validation Example</title>
<style>
.error {
color: red;
}
</style>
</head>
<body>
<h1>User Registration</h1>
<form id="registrationForm" onsubmit="return validateForm()">
<label for="name">Name:</label>
<input type="text" id="name" name="name">
<span id="nameError" class="error"></span>
<br>
<label for="email">Email:</label>
<input type="text" id="email" name="email">
<span id="emailError" class="error"></span>
<br>
<label for="password">Password:</label>
<input type="password" id="password" name="password">
<span id="passwordError" class="error"></span>
<br>
<label for="confirmPassword">Confirm Password:</label>
<input type="password" id="confirmPassword" name="confirmPassword">
<span id="confirmPasswordError" class="error"></span>
<br>
<label for="phone">Phone Number:</label>
<input type="text" id="phone" name="phone">
<span id="phoneError" class="error"></span>
<br>
<button type="submit">Register</button>
</form>
<script src="advancedValidation.js"></script>
</body>
</html>
advancedValidation.js
Five fields, five rule groups, two helper functions. Notice the password rules are evaluated in priority order: empty check first, then length check, then the confirm-match check on the second field.
function validateForm() {
// Clear previous error messages
document.getElementById("nameError").textContent = "";
document.getElementById("emailError").textContent = "";
document.getElementById("passwordError").textContent = "";
document.getElementById("confirmPasswordError").textContent = "";
document.getElementById("phoneError").textContent = "";
// Get form values
let name = document.getElementById("name").value;
let email = document.getElementById("email").value;
let password = document.getElementById("password").value;
let confirmPassword = document.getElementById("confirmPassword").value;
let phone = document.getElementById("phone").value;
let isValid = true;
// Validate name
if (name === "") {
document.getElementById("nameError").textContent = "Name is required";
isValid = false;
}
// Validate email
if (email === "") {
document.getElementById("emailError").textContent = "Email is required";
isValid = false;
} else if (!validateEmail(email)) {
document.getElementById("emailError").textContent = "Invalid email format";
isValid = false;
}
// Validate password
if (password === "") {
document.getElementById("passwordError").textContent = "Password is required";
isValid = false;
} else if (password.length < 8) {
document.getElementById("passwordError").textContent = "Password must be at least 8 characters";
isValid = false;
}
// Validate confirm password
if (confirmPassword === "") {
document.getElementById("confirmPasswordError").textContent = "Confirm Password is required";
isValid = false;
} else if (password !== confirmPassword) {
document.getElementById("confirmPasswordError").textContent = "Passwords do not match";
isValid = false;
}
// Validate phone number
if (phone === "") {
document.getElementById("phoneError").textContent = "Phone number is required";
isValid = false;
} else if (!validatePhone(phone)) {
document.getElementById("phoneError").textContent = "Invalid phone number format";
isValid = false;
}
return isValid;
}
// Helper function to validate email format
function validateEmail(email) {
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailPattern.test(email);
}
// Helper function to validate phone number format
function validatePhone(phone) {
const phonePattern = /^[0-9]{10}$/;
return phonePattern.test(phone);
}
Key rules at a glance:
| Field | Rule | How it's checked |
|---|---|---|
| Name | Not empty | name === "" |
| Not empty + valid format | validateEmail() with regex | |
| Password | Not empty + ≥ 8 characters | password.length < 8 |
| Confirm Password | Not empty + matches password | password !== confirmPassword |
| Phone | Not empty + exactly 10 digits | validatePhone() with /^[0-9]{10}$/ |
Phone regex breakdown: /^[0-9]{10}$/ matches a string that is exactly 10 consecutive decimal digits, nothing more. It rejects hyphens, spaces, and country-code prefixes — adjust the pattern if your application needs to accept those forms.
🧪 Try It Yourself
Task: Build the advanced registration form from Chapter 3 and deliberately trigger every error message.
- Save the HTML as
registration.htmland the JS asadvancedValidation.jsin the same folder. - Open
registration.htmlin your browser. - Click Register with all fields empty — you should see five simultaneous error messages.
- Fill in a name, then enter
notanemailin the email field — you should see "Invalid email format". - Enter a 5-character password — you should see "Password must be at least 8 characters".
- Enter a valid password in the first field and a different string in the confirm field — you should see "Passwords do not match".
- Enter
12345in the phone field — you should see "Invalid phone number format".
Starter snippet — use this as your advancedValidation.js starting point and fill in the missing validatePhone helper:
function validateForm() {
document.getElementById("phoneError").textContent = "";
let phone = document.getElementById("phone").value;
let isValid = true;
if (phone === "") {
document.getElementById("phoneError").textContent = "Phone number is required";
isValid = false;
} else if (!validatePhone(phone)) {
document.getElementById("phoneError").textContent = "Invalid phone number format";
isValid = false;
}
return isValid;
}
function validatePhone(phone) {
// TODO: write the regex that matches exactly 10 digits
}
Success criterion: After completing all seven steps above, every field should produce its specific error message — and when all fields are correctly filled, the form submits (or the page reloads) without any error messages appearing.
🔍 Checkpoint Quiz
Q1. Why should you never rely solely on client-side JavaScript validation to protect your application?
A) Because JavaScript runs too slowly to catch errors
B) Because users can disable JavaScript in their browser and bypass it entirely
C) Because server-side validation is slower and less accurate
D) Because regex patterns only work on the server
Q2. Given this snippet, what does it print to the console when email = "hello@" is passed in?
function validateEmail(email) {
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailPattern.test(email);
}
console.log(validateEmail("hello@"));
A) true
B) false
C) undefined
D) A syntax error is thrown
Q3. In advancedValidation.js, the confirm-password block only runs the "Passwords do not match" check inside an else if. What is the benefit of this structure compared to using a second independent if?
A) It runs faster because else if is a compiled keyword
B) It prevents showing "Passwords do not match" when the field is already flagged as empty
C) It makes the isValid flag reset itself automatically
D) There is no practical difference between else if and if here
Q4. You need to accept phone numbers in the format +91-9876543210 (country code + hyphen + 10 digits). The current pattern /^[0-9]{10}$/ rejects this. How would you update validatePhone to accept this format?
A1. B — JavaScript runs in the browser and a malicious or technically savvy user can disable it, open browser DevTools, or craft a raw HTTP request to send any data they choose directly to your server, bypassing all client-side checks.
A2. B — The string "hello@" has no characters after the @ and no dot-separated TLD, so the pattern /^[^\s@]+@[^\s@]+\.[^\s@]+$/ does not match and .test() returns false.
A3. B — If confirmPassword === "", the first branch sets the error to "Confirm Password is required" and sets isValid = false. The else if ensures the "Passwords do not match" message is only shown when the field actually has content — preventing two overlapping error messages on the same span.
A4. Update the regex to allow an optional country code and hyphen before the 10 digits: const phonePattern = /^(\+\d{1,3}-)?[0-9]{10}$/;. The group (\+\d{1,3}-)? makes the + sign, 1–3 digits, and hyphen optional, while the rest still enforces exactly 10 trailing digits.
🪞 Recap
- Client-side validation runs in the browser and catches errors instantly; server-side validation is the authoritative layer that cannot be bypassed.
- The pattern
onsubmit="return validateForm()"lets a JavaScript function cancel form submission by returningfalse. - Clearing error spans at the start of every
validateForm()call prevents stale messages from lingering across submission attempts. - The email regex
/^[^\s@]+@[^\s@]+\.[^\s@]+$/and phone regex/^[0-9]{10}$/are concise, pragmatic patterns for common format checks. - Password rules should be evaluated in priority order: empty → too short → confirm mismatch — so each field shows only the most relevant error at a time.
📚 Further Reading
- MDN — Client-side form validation — the source of truth on constraint validation API, built-in HTML5 attributes, and custom JS patterns
- MDN — Regular Expressions — complete reference for the regex syntax used in
validateEmailandvalidatePhone - OWASP Input Validation Cheat Sheet — why server-side validation is non-negotiable from a security standpoint
- ⬅️ Previous: JavaScript DOM
- ➡️ Next: Joint Query, Nested Query, Filtering Data