<!-- Complete Registration Form Example -->
<form id="registration-form" novalidate>
<h2>Create Account</h2>
<div class="form-group">
<label for="fullname">Full Name</label>
<input
type="text"
id="fullname"
name="fullname"
required
aria-describedby="fullname-error"
>
<span class="error" id="fullname-error" role="alert" aria-live="polite"></span>
</div>
<div class="form-group">
<label for="email">Email Address</label>
<input
type="email"
id="email"
name="email"
required
aria-describedby="email-error"
>
<span class="error" id="email-error" role="alert" aria-live="polite"></span>
</div>
<div class="form-group">
<label for="username">Username</label>
<input
type="text"
id="username"
name="username"
required
minlength="3"
maxlength="20"
pattern="^[a-zA-Z0-9_]+$"
aria-describedby="username-hint username-error"
>
<span class="hint" id="username-hint">3-20 characters, letters, numbers, and underscores only</span>
<span class="error" id="username-error" role="alert" aria-live="polite"></span>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
type="password"
id="password"
name="password"
required
minlength="8"
aria-describedby="password-hint password-error"
>
<span class="hint" id="password-hint">At least 8 characters with uppercase, lowercase, number, and special character</span>
<div class="password-strength-meter">
<div id="strength-meter-bar"></div>
</div>
<span id="strength-text"></span>
<span class="error" id="password-error" role="alert" aria-live="polite"></span>
</div>
<div class="form-group">
<label for="confirm-password">Confirm Password</label>
<input
type="password"
id="confirm-password"
name="confirm-password"
required
aria-describedby="confirm-password-error"
>
<span class="error" id="confirm-password-error" role="alert" aria-live="polite"></span>
</div>
<div class="form-group">
<label for="dob">Date of Birth</label>
<input
type="date"
id="dob"
name="dob"
required
aria-describedby="dob-error"
>
<span class="error" id="dob-error" role="alert" aria-live="polite"></span>
</div>
<div class="form-group checkbox-group">
<input
type="checkbox"
id="terms"
name="terms"
required
aria-describedby="terms-error"
>
<label for="terms">I agree to the <a href="#">Terms and Conditions</a></label>
<span class="error" id="terms-error" role="alert" aria-live="polite"></span>
</div>
<button type="submit" class="submit-button">Create Account</button>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('registration-form');
// Get all form elements
const fullnameInput = document.getElementById('fullname');
const emailInput = document.getElementById('email');
const usernameInput = document.getElementById('username');
const passwordInput = document.getElementById('password');
const confirmPasswordInput = document.getElementById('confirm-password');
const dobInput = document.getElementById('dob');
const termsCheckbox = document.getElementById('terms');
// Get all error elements
const fullnameError = document.getElementById('fullname-error');
const emailError = document.getElementById('email-error');
const usernameError = document.getElementById('username-error');
const passwordError = document.getElementById('password-error');
const confirmPasswordError = document.getElementById('confirm-password-error');
const dobError = document.getElementById('dob-error');
const termsError = document.getElementById('terms-error');
// Get password strength elements
const strengthMeter = document.getElementById('strength-meter-bar');
const strengthText = document.getElementById('strength-text');
// Fullname validation
fullnameInput.addEventListener('input', function() {
if (fullnameInput.validity.valueMissing) {
showError(fullnameInput, fullnameError, 'Please enter your full name');
} else if (fullnameInput.value.trim().split(' ').length < 2) {
showError(fullnameInput, fullnameError, 'Please enter your first and last name');
} else {
clearError(fullnameInput, fullnameError);
}
});
// Email validation
emailInput.addEventListener('input', function() {
if (emailInput.validity.valueMissing) {
showError(emailInput, emailError, 'Please enter your email address');
} else if (emailInput.validity.typeMismatch) {
showError(emailInput, emailError, 'Please enter a valid email address');
} else {
clearError(emailInput, emailError);
}
});
// Username validation
usernameInput.addEventListener('input', function() {
if (usernameInput.validity.valueMissing) {
showError(usernameInput, usernameError, 'Please enter a username');
} else if (usernameInput.validity.tooShort) {
showError(usernameInput, usernameError, 'Username must be at least 3 characters');
} else if (usernameInput.validity.tooLong) {
showError(usernameInput, usernameError, 'Username must be no more than 20 characters');
} else if (usernameInput.validity.patternMismatch) {
showError(usernameInput, usernameError, 'Username can only contain letters, numbers, and underscores');
} else {
clearError(usernameInput, usernameError);
}
});
// Password validation and strength meter
passwordInput.addEventListener('input', function() {
// Update password strength meter
const password = passwordInput.value;
const strength = calculatePasswordStrength(password);
// Update strength meter
strengthMeter.className = '';
if (strength === 0) {
strengthMeter.style.width = '0';
strengthText.textContent = '';
} else if (strength < 40) {
strengthMeter.classList.add('strength-weak');
strengthText.textContent = 'Weak';
} else if (strength < 80) {
strengthMeter.classList.add('strength-medium');
strengthText.textContent = 'Medium';
} else {
strengthMeter.classList.add('strength-strong');
strengthText.textContent = 'Strong';
}
// Validate password
if (passwordInput.validity.valueMissing) {
showError(passwordInput, passwordError, 'Please enter a password');
} else if (passwordInput.validity.tooShort) {
showError(passwordInput, passwordError, 'Password must be at least 8 characters');
} else if (!/[A-Z]/.test(password)) {
showError(passwordInput, passwordError, 'Password must include at least one uppercase letter');
} else if (!/[a-z]/.test(password)) {
showError(passwordInput, passwordError, 'Password must include at least one lowercase letter');
} else if (!/[0-9]/.test(password)) {
showError(passwordInput, passwordError, 'Password must include at least one number');
} else if (!/[^A-Za-z0-9]/.test(password)) {
showError(passwordInput, passwordError, 'Password must include at least one special character');
} else {
clearError(passwordInput, passwordError);
}
// Also validate confirm password if it has a value
if (confirmPasswordInput.value) {
validatePasswordMatch();
}
});
// Confirm password validation
confirmPasswordInput.addEventListener('input', validatePasswordMatch);
function validatePasswordMatch() {
if (confirmPasswordInput.validity.valueMissing) {
showError(confirmPasswordInput, confirmPasswordError, 'Please confirm your password');
} else if (confirmPasswordInput.value !== passwordInput.value) {
showError(confirmPasswordInput, confirmPasswordError, 'Passwords do not match');
} else {
clearError(confirmPasswordInput, confirmPasswordError);
}
}
// Date of birth validation
dobInput.addEventListener('input', function() {
if (dobInput.validity.valueMissing) {
showError(dobInput, dobError, 'Please enter your date of birth');
} else {
// Check if user is at least 18 years old
const dobDate = new Date(dobInput.value);
const today = new Date();
const eighteenYearsAgo = new Date(today.getFullYear() - 18, today.getMonth(), today.getDate());
if (dobDate > eighteenYearsAgo) {
showError(dobInput, dobError, 'You must be at least 18 years old to register');
} else {
clearError(dobInput, dobError);
}
}
});
// Terms checkbox validation
termsCheckbox.addEventListener('change', function() {
if (!termsCheckbox.checked) {
showError(termsCheckbox, termsError, 'You must agree to the terms and conditions');
} else {
clearError(termsCheckbox, termsError);
}
});
// Form submission
form.addEventListener('submit', function(event) {
// Prevent default form submission
event.preventDefault();
// Trigger validation for all fields
fullnameInput.dispatchEvent(new Event('input'));
emailInput.dispatchEvent(new Event('input'));
usernameInput.dispatchEvent(new Event('input'));
passwordInput.dispatchEvent(new Event('input'));
confirmPasswordInput.dispatchEvent(new Event('input'));
dobInput.dispatchEvent(new Event('input'));
termsCheckbox.dispatchEvent(new Event('change'));
// Check if the form is valid
if (form.checkValidity() &&
!fullnameError.textContent &&
!emailError.textContent &&
!usernameError.textContent &&
!passwordError.textContent &&
!confirmPasswordError.textContent &&
!dobError.textContent &&
!termsError.textContent) {
// Form is valid - would normally submit to server
console.log('Form submitted successfully!');
alert('Registration successful!');
// In a real application, you would submit the form data to your server here
// form.submit();
}
});
// Helper function to show error message
function showError(input, errorElement, message) {
errorElement.textContent = message;
input.setAttribute('aria-invalid', 'true');
input.classList.add('invalid');
input.classList.remove('valid');
}
// Helper function to clear error message
function clearError(input, errorElement) {
errorElement.textContent = '';
input.setAttribute('aria-invalid', 'false');
input.classList.remove('invalid');
input.classList.add('valid');
}
// Calculate password strength
function calculatePasswordStrength(password) {
// Start with a base score
let score = 0;
// If password is empty, return 0
if (password.length === 0) return 0;
// Score for length
score += Math.min(password.length * 4, 40);
// Score for character variation
const variations = {
digits: /\d/.test(password),
lower: /[a-z]/.test(password),
upper: /[A-Z]/.test(password),
nonWords: /\W/.test(password)
};
let variationCount = 0;
for (const check in variations) {
variationCount += variations[check] ? 1 : 0;
}
score += variationCount * 10;
// Bonus for mixing character types
if (variationCount > 2) {
score += 10;
}
// Penalize for repeating patterns
if (/(.)\1\1/.test(password)) {
score -= 20;
}
// Penalize for sequential characters
if (/(?:012|123|234|345|456|567|678|789|abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz)/i.test(password)) {
score -= 15;
}
// Cap the score at 100
return Math.max(0, Math.min(100, score));
}
});
</script>
<!-- Multi-Step Form HTML -->
<form id="multi-step-form" novalidate>
<div class="form-progress">
<div class="progress-step active" data-step="1">Personal Info</div>
<div class="progress-step" data-step="2">Contact Details</div>
<div class="progress-step" data-step="3">Account Setup</div>
<div class="progress-step" data-step="4">Confirmation</div>
</div>
<!-- Step 1: Personal Information -->
<div class="form-step active" id="step-1">
<h2>Personal Information</h2>
<div class="form-group">
<label for="first-name">First Name</label>
<input type="text" id="first-name" name="first-name" required>
<span class="error" id="first-name-error"></span>
</div>
<div class="form-group">
<label for="last-name">Last Name</label>
<input type="text" id="last-name" name="last-name" required>
<span class="error" id="last-name-error"></span>
</div>
<div class="form-group">
<label for="birth-date">Date of Birth</label>
<input type="date" id="birth-date" name="birth-date" required>
<span class="error" id="birth-date-error"></span>
</div>
<div class="form-group">
<label for="gender">Gender</label>
<select id="gender" name="gender" required>
<option value="">Select Gender</option>
<option value="male">Male</option>
<option value="female">Female</option>
<option value="non-binary">Non-binary</option>
<option value="other">Other</option>
<option value="prefer-not">Prefer not to say</option>
</select>
<span class="error" id="gender-error"></span>
</div>
<div class="form-buttons">
<button type="button" class="next-button" data-next="2">Next</button>
</div>
</div>
<!-- Step 2: Contact Details -->
<div class="form-step" id="step-2">
<h2>Contact Details</h2>
<div class="form-group">
<label for="email">Email Address</label>
<input type="email" id="email" name="email" required>
<span class="error" id="email-error"></span>
</div>
<div class="form-group">
<label for="phone">Phone Number</label>
<input type="tel" id="phone" name="phone" required pattern="[0-9]{10}">
<span class="hint">10-digit number without spaces or dashes</span>
<span class="error" id="phone-error"></span>
</div>
<div class="form-group">
<label for="address">Address</label>
<textarea id="address" name="address" required></textarea>
<span class="error" id="address-error"></span>
</div>
<div class="form-buttons">
<button type="button" class="back-button" data-back="1">Back</button>
<button type="button" class="next-button" data-next="3">Next</button>
</div>
</div>
<!-- Step 3: Account Setup -->
<div class="form-step" id="step-3">
<h2>Account Setup</h2>
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" required minlength="5" maxlength="20">
<span class="hint">5-20 characters, letters and numbers only</span>
<span class="error" id="username-error"></span>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" required minlength="8">
<span class="hint">At least 8 characters with uppercase, lowercase, number, and special character</span>
<span class="error" id="password-error"></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-buttons">
<button type="button" class="back-button" data-back="2">Back</button>
<button type="button" class="next-button" data-next="4">Next</button>
</div>
</div>
<!-- Step 4: Confirmation -->
<div class="form-step" id="step-4">
<h2>Confirm Your Information</h2>
<div id="summary">
<!-- Summary content will be populated by JavaScript -->
</div>
<div class="form-group checkbox-group">
<input type="checkbox" id="terms" name="terms" required>
<label for="terms">I agree to the <a href="#">Terms and Conditions</a></label>
<span class="error" id="terms-error"></span>
</div>
<div class="form-buttons">
<button type="button" class="back-button" data-back="3">Back</button>
<button type="submit" class="submit-button">Submit</button>
</div>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('multi-step-form');
const steps = document.querySelectorAll('.form-step');
const progressSteps = document.querySelectorAll('.progress-step');
// Initialize form validation data structure
const formValidation = {
step1: false,
step2: false,
step3: false,
step4: false
};
// Next button event listeners
const nextButtons = document.querySelectorAll('.next-button');
nextButtons.forEach(button => {
button.addEventListener('click', function() {
const currentStep = parseInt(button.getAttribute('data-next')) - 1;
const nextStep = parseInt(button.getAttribute('data-next'));
// Validate current step
if (validateStep(currentStep)) {
formValidation['step' + currentStep] = true;
// Move to next step
showStep(nextStep);
// If moving to confirmation step, populate summary
if (nextStep === 4) {
populateSummary();
}
}
});
});
// Back button event listeners
const backButtons = document.querySelectorAll('.back-button');
backButtons.forEach(button => {
button.addEventListener('click', function() {
const prevStep = parseInt(button.getAttribute('data-back'));
showStep(prevStep);
});
});
// Form submission
form.addEventListener('submit', function(event) {
event.preventDefault();
// Validate step 4
if (validateStep(4)) {
formValidation.step4 = true;
// If all steps are valid, submit the form
if (formValidation.step1 && formValidation.step2 && formValidation.step3 && formValidation.step4) {
alert('Form submitted successfully!');
console.log('Form data:', new FormData(form));
// In a real application, you would submit the form data to your server here
// form.submit();
}
}
});
// Function to show a specific step
function showStep(stepNumber) {
// Hide all steps
steps.forEach(step => {
step.classList.remove('active');
});
// Update progress indicators
progressSteps.forEach(step => {
const stepNum = parseInt(step.getAttribute('data-step'));
if (stepNum < stepNumber) {
step.classList.add('completed');
step.classList.remove('active');
} else if (stepNum === stepNumber) {
step.classList.add('active');
step.classList.remove('completed');
} else {
step.classList.remove('active', 'completed');
}
});
// Show the current step
document.getElementById('step-' + stepNumber).classList.add('active');
}
// Validate each step
function validateStep(stepNumber) {
let isValid = true;
switch(stepNumber) {
case 1:
// Validate personal information
isValid = validateField('first-name', 'First name is required') &&
validateField('last-name', 'Last name is required') &&
validateField('birth-date', 'Date of birth is required') &&
validateField('gender', 'Please select your gender');
break;
case 2:
// Validate contact details
isValid = validateField('email', 'Email address is required') &&
validateEmail() &&
validateField('phone', 'Phone number is required') &&
validatePhone() &&
validateField('address', 'Address is required');
break;
case 3:
// Validate account setup
isValid = validateField('username', 'Username is required') &&
validateUsername() &&
validateField('password', 'Password is required') &&
validatePassword() &&
validatePasswordMatch();
break;
case 4:
// Validate terms agreement
isValid = validateTerms();
break;
}
return isValid;
}
// Validate a required field
function validateField(fieldId, errorMessage) {
const field = document.getElementById(fieldId);
const errorElement = document.getElementById(fieldId + '-error');
if (!field.value.trim()) {
errorElement.textContent = errorMessage;
field.classList.add('invalid');
return false;
} else {
errorElement.textContent = '';
field.classList.remove('invalid');
field.classList.add('valid');
return true;
}
}
// Validate email format
function validateEmail() {
const email = document.getElementById('email');
const errorElement = document.getElementById('email-error');
if (email.value && !isValidEmail(email.value)) {
errorElement.textContent = 'Please enter a valid email address';
email.classList.add('invalid');
return false;
}
return true;
}
// Validate phone format
function validatePhone() {
const phone = document.getElementById('phone');
const errorElement = document.getElementById('phone-error');
if (phone.value && !phone.validity.valid) {
errorElement.textContent = 'Please enter a valid 10-digit phone number';
phone.classList.add('invalid');
return false;
}
return true;
}
// Validate username
function validateUsername() {
const username = document.getElementById('username');
const errorElement = document.getElementById('username-error');
if (username.value && username.value.length < 5) {
errorElement.textContent = 'Username must be at least 5 characters';
username.classList.add('invalid');
return false;
} else if (username.value && !/^[a-zA-Z0-9]+$/.test(username.value)) {
errorElement.textContent = 'Username can only contain letters and numbers';
username.classList.add('invalid');
return false;
}
return true;
}
// Validate password
function validatePassword() {
const password = document.getElementById('password');
const errorElement = document.getElementById('password-error');
if (password.value && password.value.length < 8) {
errorElement.textContent = 'Password must be at least 8 characters';
password.classList.add('invalid');
return false;
} else if (password.value && !isStrongPassword(password.value)) {
errorElement.textContent = 'Password must include uppercase, lowercase, number, and special character';
password.classList.add('invalid');
return false;
}
return true;
}
// Validate password match
function validatePasswordMatch() {
const password = document.getElementById('password');
const confirmPassword = document.getElementById('confirm-password');
const errorElement = document.getElementById('confirm-password-error');
if (!confirmPassword.value) {
errorElement.textContent = 'Please confirm your password';
confirmPassword.classList.add('invalid');
return false;
} else if (confirmPassword.value !== password.value) {
errorElement.textContent = 'Passwords do not match';
confirmPassword.classList.add('invalid');
return false;
} else {
errorElement.textContent = '';
confirmPassword.classList.remove('invalid');
confirmPassword.classList.add('valid');
return true;
}
}
// Validate terms agreement
function validateTerms() {
const terms = document.getElementById('terms');
const errorElement = document.getElementById('terms-error');
if (!terms.checked) {
errorElement.textContent = 'You must agree to the terms and conditions';
return false;
} else {
errorElement.textContent = '';
return true;
}
}
// Populate the summary section
function populateSummary() {
const summary = document.getElementById('summary');
const firstName = document.getElementById('first-name').value;
const lastName = document.getElementById('last-name').value;
const birthDate = document.getElementById('birth-date').value;
const gender = document.getElementById('gender').value;
const email = document.getElementById('email').value;
const phone = document.getElementById('phone').value;
const address = document.getElementById('address').value;
const username = document.getElementById('username').value;
summary.innerHTML = `
<div class="summary-section">
<h3>Personal Information</h3>
<p><strong>Name:</strong> ${firstName} ${lastName}</p>
<p><strong>Date of Birth:</strong> ${formatDate(birthDate)}</p>
<p><strong>Gender:</strong> ${formatGender(gender)}</p>
</div>
<div class="summary-section">
<h3>Contact Details</h3>
<p><strong>Email:</strong> ${email}</p>
<p><strong>Phone:</strong> ${formatPhone(phone)}</p>
<p><strong>Address:</strong> ${address}</p>
</div>
<div class="summary-section">
<h3>Account Information</h3>
<p><strong>Username:</strong> ${username}</p>
<p><strong>Password:</strong> ********</p>
</div>
`;
}
// Helper function: Format date
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
}
// Helper function: Format gender
function formatGender(gender) {
if (gender === 'prefer-not') {
return 'Prefer not to say';
}
return gender.charAt(0).toUpperCase() + gender.slice(1);
}
// Helper function: Format phone
function formatPhone(phone) {
return phone.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
}
// Helper function: Validate email format
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
// Helper function: Check password strength
function isStrongPassword(password) {
// Password must contain at least one uppercase letter, one lowercase letter,
// one number, and one special character
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?])[A-Za-z\d!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]{8,}$/;
return passwordRegex.test(password);
}
});
</script>