Introduction to Form Validation
Form validation ensures that users enter data in the correct format and that required fields are filled before submission. HTML5 provides built-in validation attributes, while JavaScript allows for custom validation logic.
Built-in HTML5 Validation
HTML5 offers several validation attributes that work without any JavaScript:
Required Fields
html
<!-- Basic required validation --> <label for="email">Email (required):</label> <input type="email" id="email" name="email" required> <label for="password">Password (required):</label> <input type="password" id="password" name="password" required> <label for="terms"> <input type="checkbox" id="terms" name="terms" required> I agree to the terms and conditions </label>
Length Validation
html
<!-- Minimum and maximum length -->
<label for="username">Username (3-20 characters):</label>
<input type="text"
id="username"
name="username"
minlength="3"
maxlength="20"
required>
<!-- Textarea with character limit -->
<label for="bio">Bio (max 500 characters):</label>
<textarea id="bio"
name="bio"
maxlength="500"
placeholder="Tell us about yourself..."></textarea>
Numeric Validation
html
<!-- Number range validation -->
<label for="age">Age (18-100):</label>
<input type="number"
id="age"
name="age"
min="18"
max="100"
required>
<!-- Step validation -->
<label for="price">Price (increments of 0.50):</label>
<input type="number"
id="price"
name="price"
min="0"
step="0.50"
placeholder="0.00">
<!-- Range slider -->
<label for="rating">Rating (1-10):</label>
<input type="range"
id="rating"
name="rating"
min="1"
max="10"
value="5">
<output for="rating">5</output>
Pattern Validation
html
<!-- Phone number pattern -->
<label for="phone">Phone (XXX-XXX-XXXX):</label>
<input type="tel"
id="phone"
name="phone"
pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}"
placeholder="123-456-7890"
title="Please enter phone in format: XXX-XXX-XXXX">
<!-- Username pattern -->
<label for="username">Username (letters, numbers, underscore only):</label>
<input type="text"
id="username"
name="username"
pattern="[a-zA-Z0-9_]+"
title="Username can only contain letters, numbers, and underscores"
required>
<!-- Password complexity -->
<label for="password">Password (min 8 chars, 1 uppercase, 1 number):</label>
<input type="password"
id="password"
name="password"
pattern="(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d@$!%*?&]{8,}"
title="Password must be at least 8 characters with 1 uppercase letter and 1 number"
required>
Input Type Validation
html
<!-- Automatic email validation -->
<label for="email">Email:</label>
<input type="email"
id="email"
name="email"
placeholder="user@example.com"
required>
<!-- URL validation -->
<label for="website">Website:</label>
<input type="url"
id="website"
name="website"
placeholder="https://example.com">
<!-- Date validation -->
<label for="start-date">Start Date:</label>
<input type="date"
id="start-date"
name="start-date"
min="2024-01-01"
max="2025-12-31"
required>
Custom Validation with JavaScript
For more complex validation logic, JavaScript provides powerful tools:
Basic Custom Validation
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom Validation Example</title>
<style>
.error {
color: red;
font-size: 0.9em;
margin-top: 5px;
}
.invalid {
border-color: red;
}
.valid {
border-color: green;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
</style>
</head>
<body>
<form id="registration-form" novalidate>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<div class="error" id="email-error"></div>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
<div class="error" id="password-error"></div>
</div>
<div class="form-group">
<label for="confirm-password">Confirm Password:</label>
<input type="password" id="confirm-password" name="confirm-password" required>
<div class="error" id="confirm-password-error"></div>
</div>
<button type="submit">Register</button>
</form>
<script>
const form = document.getElementById('registration-form');
const emailInput = document.getElementById('email');
const passwordInput = document.getElementById('password');
const confirmPasswordInput = document.getElementById('confirm-password');
// Custom validation functions
function validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
function validatePassword(password) {
// At least 8 characters, 1 uppercase, 1 lowercase, 1 number
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d@$!%*?&]{8,}$/;
return passwordRegex.test(password);
}
function showError(inputElement, message) {
const errorElement = document.getElementById(inputElement.id + '-error');
errorElement.textContent = message;
inputElement.classList.add('invalid');
inputElement.classList.remove('valid');
}
function showSuccess(inputElement) {
const errorElement = document.getElementById(inputElement.id + '-error');
errorElement.textContent = '';
inputElement.classList.add('valid');
inputElement.classList.remove('invalid');
}
// Real-time validation
emailInput.addEventListener('blur', function() {
if (this.value === '') {
showError(this, 'Email is required');
} else if (!validateEmail(this.value)) {
showError(this, 'Please enter a valid email address');
} else {
showSuccess(this);
}
});
passwordInput.addEventListener('blur', function() {
if (this.value === '') {
showError(this, 'Password is required');
} else if (!validatePassword(this.value)) {
showError(this, 'Password must be at least 8 characters with 1 uppercase, 1 lowercase, and 1 number');
} else {
showSuccess(this);
}
});
confirmPasswordInput.addEventListener('blur', function() {
if (this.value === '') {
showError(this, 'Please confirm your password');
} else if (this.value !== passwordInput.value) {
showError(this, 'Passwords do not match');
} else {
showSuccess(this);
}
});
// Form submission validation
form.addEventListener('submit', function(e) {
e.preventDefault();
let isValid = true;
// Validate all fields
if (!validateEmail(emailInput.value)) {
showError(emailInput, 'Please enter a valid email address');
isValid = false;
}
if (!validatePassword(passwordInput.value)) {
showError(passwordInput, 'Password must be at least 8 characters with 1 uppercase, 1 lowercase, and 1 number');
isValid = false;
}
if (confirmPasswordInput.value !== passwordInput.value) {
showError(confirmPasswordInput, 'Passwords do not match');
isValid = false;
}
if (isValid) {
alert('Form submitted successfully!');
// Here you would normally submit the form to the server
}
});
</script>
</body>
</html>
Advanced Validation Techniques
Using the Constraint Validation API
html
<script>
// Using HTML5 Constraint Validation API
function customValidation() {
const input = document.getElementById('custom-input');
// Check if input is valid
if (input.validity.valid) {
// Input is valid
input.setCustomValidity('');
} else {
// Set custom error message
if (input.validity.valueMissing) {
input.setCustomValidity('This field is required');
} else if (input.validity.tooShort) {
input.setCustomValidity('Input is too short');
} else if (input.validity.patternMismatch) {
input.setCustomValidity('Input format is incorrect');
}
}
// Report validity
input.reportValidity();
}
</script>
Async Validation (Username Check)
html
<div class="form-group">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
<div class="error" id="username-error"></div>
<div class="loading" id="username-loading" style="display: none;">Checking availability...</div>
</div>
<script>
let usernameTimeout;
document.getElementById('username').addEventListener('input', function() {
clearTimeout(usernameTimeout);
const username = this.value;
if (username.length >= 3) {
// Debounce the API call
usernameTimeout = setTimeout(() => {
checkUsernameAvailability(username);
}, 500);
}
});
async function checkUsernameAvailability(username) {
const loadingElement = document.getElementById('username-loading');
const errorElement = document.getElementById('username-error');
const inputElement = document.getElementById('username');
loadingElement.style.display = 'block';
try {
// Simulate API call
const response = await fetch(`/api/check-username?username=${username}`);
const data = await response.json();
if (data.available) {
showSuccess(inputElement);
} else {
showError(inputElement, 'Username is already taken');
}
} catch (error) {
showError(inputElement, 'Error checking username availability');
} finally {
loadingElement.style.display = 'none';
}
}
</script>
Complete Validation Example
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Complete Form Validation</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 500px;
margin: 20px auto;
padding: 20px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input, textarea {
width: 100%;
padding: 10px;
border: 2px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
transition: border-color 0.3s;
}
input:focus, textarea:focus {
outline: none;
border-color: #007bff;
}
.invalid {
border-color: #dc3545;
}
.valid {
border-color: #28a745;
}
.error {
color: #dc3545;
font-size: 0.9em;
margin-top: 5px;
display: block;
}
.success {
color: #28a745;
font-size: 0.9em;
margin-top: 5px;
display: block;
}
button {
background-color: #007bff;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #0056b3;
}
button:disabled {
background-color: #6c757d;
cursor: not-allowed;
}
.strength-meter {
height: 5px;
background-color: #f0f0f0;
border-radius: 3px;
margin-top: 5px;
overflow: hidden;
}
.strength-bar {
height: 100%;
transition: width 0.3s, background-color 0.3s;
}
.strength-weak { background-color: #dc3545; }
.strength-fair { background-color: #ffc107; }
.strength-good { background-color: #28a745; }
.strength-strong { background-color: #007bff; }
</style>
</head>
<body>
<form id="complete-form" novalidate>
<h2>Registration Form</h2>
<div class="form-group">
<label for="full-name">Full Name:</label>
<input type="text" id="full-name" name="full-name" required>
<span class="error" id="full-name-error"></span>
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<span class="error" id="email-error"></span>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
<div class="strength-meter">
<div class="strength-bar" id="strength-bar"></div>
</div>
<span class="error" id="password-error"></span>
<span id="password-strength"></span>
</div>
<div class="form-group">
<label for="confirm-password">Confirm Password:</label>
<input type="password" id="confirm-password" name="confirm-password" required>
<span class="error" id="confirm-password-error"></span>
</div>
<div class="form-group">
<label for="age">Age:</label>
<input type="number" id="age" name="age" min="13" max="120" required>
<span class="error" id="age-error"></span>
</div>
<button type="submit" id="submit-btn">Register</button>
</form>
<script>
const form = document.getElementById('complete-form');
const inputs = form.querySelectorAll('input[required]');
// Validation rules
const validators = {
'full-name': {
validate: (value) => value.trim().length >= 2,
message: 'Full name must be at least 2 characters long'
},
'email': {
validate: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
message: 'Please enter a valid email address'
},
'password': {
validate: (value) => value.length >= 8 && /(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(value),
message: 'Password must be at least 8 characters with uppercase, lowercase, and number'
},
'confirm-password': {
validate: (value) => value === document.getElementById('password').value,
message: 'Passwords do not match'
},
'age': {
validate: (value) => value >= 13 && value <= 120,
message: 'Age must be between 13 and 120'
}
};
// Password strength checker
function checkPasswordStrength(password) {
let strength = 0;
const strengthBar = document.getElementById('strength-bar');
const strengthText = document.getElementById('password-strength');
if (password.length >= 8) strength++;
if (/[a-z]/.test(password)) strength++;
if (/[A-Z]/.test(password)) strength++;
if (/\d/.test(password)) strength++;
if (/[^a-zA-Z\d]/.test(password)) strength++;
const strengthLevels = ['Very Weak', 'Weak', 'Fair', 'Good', 'Strong'];
const strengthClasses = ['strength-weak', 'strength-weak', 'strength-fair', 'strength-good', 'strength-strong'];
strengthBar.style.width = `${(strength / 5) * 100}%`;
strengthBar.className = `strength-bar ${strengthClasses[strength]}`;
strengthText.textContent = strengthLevels[strength] || '';
strengthText.className = strength >= 3 ? 'success' : 'error';
}
// Validation functions
function showError(input, message) {
const errorElement = document.getElementById(`${input.id}-error`);
errorElement.textContent = message;
input.classList.add('invalid');
input.classList.remove('valid');
}
function showSuccess(input) {
const errorElement = document.getElementById(`${input.id}-error`);
errorElement.textContent = '';
input.classList.add('valid');
input.classList.remove('invalid');
}
function validateInput(input) {
const validator = validators[input.id];
if (!validator) return true;
if (input.value.trim() === '') {
showError(input, 'This field is required');
return false;
}
if (!validator.validate(input.value)) {
showError(input, validator.message);
return false;
}
showSuccess(input);
return true;
}
// Real-time validation
inputs.forEach(input => {
input.addEventListener('blur', () => validateInput(input));
input.addEventListener('input', () => {
if (input.id === 'password') {
checkPasswordStrength(input.value);
}
if (input.classList.contains('invalid')) {
validateInput(input);
}
});
});
// Form submission
form.addEventListener('submit', function(e) {
e.preventDefault();
let isValid = true;
inputs.forEach(input => {
if (!validateInput(input)) {
isValid = false;
}
});
if (isValid) {
alert('Registration successful!');
form.reset();
// Clear validation classes
inputs.forEach(input => {
input.classList.remove('valid', 'invalid');
document.getElementById(`${input.id}-error`).textContent = '';
});
document.getElementById('strength-bar').style.width = '0';
document.getElementById('password-strength').textContent = '';
}
});
</script>
</body>
</html>
Best Practices for Form Validation
- Validate on both client and server - Client-side validation improves UX, server-side prevents malicious data
- Provide real-time feedback - Validate on blur or input events for immediate feedback
- Use clear error messages - Be specific about what's wrong and how to fix it
- Progressive enhancement - Forms should work without JavaScript
- Accessibility - Use ARIA attributes for screen readers
- Debounce API calls - Prevent excessive server requests during typing