Form Handling and Validation
Forms are essential for collecting user input. Proper handling and validation ensure data quality and user experience.
Accessing Form Elements
Getting Form Reference
// By ID
const form = document.getElementById("myForm");
// By name
const form = document.forms["myForm"];
// By index
const form = document.forms[0];
Accessing Form Fields
const form = document.getElementById("myForm");
// By name
const email = form.elements["email"];
const password = form.elements["password"];
// By index
const firstField = form.elements[0];
// Direct access (if name is unique)
const email = form.email;
Form Events
Submit Event
const form = document.getElementById("myForm");
form.addEventListener("submit", (event) => {
event.preventDefault(); // Prevent page reload
const formData = new FormData(form);
const data = Object.fromEntries(formData);
console.log(data);
// Send to server
});
Input Event
const input = document.getElementById("email");
input.addEventListener("input", (event) => {
console.log("Current value:", event.target.value);
});
Change Event
const select = document.getElementById("country");
select.addEventListener("change", (event) => {
console.log("Selected:", event.target.value);
});
Focus and Blur Events
const input = document.getElementById("email");
input.addEventListener("focus", (event) => {
event.target.style.borderColor = "blue";
});
input.addEventListener("blur", (event) => {
event.target.style.borderColor = "gray";
});
Form Data
FormData Object
const form = document.getElementById("myForm");
const formData = new FormData(form);
// Get individual values
console.log(formData.get("email"));
console.log(formData.get("password"));
// Iterate over all entries
for (const [key, value] of formData.entries()) {
console.log(`${key}: ${value}`);
}
// Convert to object
const data = Object.fromEntries(formData);
console.log(data);
Sending Form Data
const form = document.getElementById("myForm");
form.addEventListener("submit", async (event) => {
event.preventDefault();
const formData = new FormData(form);
const response = await fetch("/api/submit", {
method: "POST",
body: formData
});
const result = await response.json();
console.log(result);
});
Validation
HTML5 Validation
<form>
<input type="email" required>
<input type="password" minlength="8" required>
<input type="number" min="0" max="100">
<input type="text" pattern="[A-Za-z]+" required>
<button type="submit">Submit</button>
</form>
JavaScript Validation
function validateEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
function validatePassword(password) {
return password.length >= 8;
}
function validateForm(formData) {
const errors = {};
if (!validateEmail(formData.email)) {
errors.email = "Invalid email";
}
if (!validatePassword(formData.password)) {
errors.password = "Password must be at least 8 characters";
}
return errors;
}
Real-Time Validation
const emailInput = document.getElementById("email");
const emailError = document.getElementById("emailError");
emailInput.addEventListener("blur", (event) => {
const email = event.target.value;
const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
if (!isValid) {
emailError.textContent = "Invalid email";
emailInput.classList.add("error");
} else {
emailError.textContent = "";
emailInput.classList.remove("error");
}
});
Practical Examples
Login Form
const loginForm = document.getElementById("loginForm");
loginForm.addEventListener("submit", async (event) => {
event.preventDefault();
const email = loginForm.email.value;
const password = loginForm.password.value;
// Validate
if (!email || !password) {
alert("Please fill in all fields");
return;
}
try {
const response = await fetch("/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password })
});
if (!response.ok) {
throw new Error("Login failed");
}
const data = await response.json();
localStorage.setItem("token", data.token);
window.location.href = "/dashboard";
} catch (error) {
alert(error.message);
}
});
Registration Form with Validation
const registrationForm = document.getElementById("registrationForm");
registrationForm.addEventListener("submit", async (event) => {
event.preventDefault();
const formData = new FormData(registrationForm);
const data = Object.fromEntries(formData);
// Validate
const errors = {};
if (!data.username || data.username.length < 3) {
errors.username = "Username must be at least 3 characters";
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
errors.email = "Invalid email";
}
if (data.password.length < 8) {
errors.password = "Password must be at least 8 characters";
}
if (data.password !== data.confirmPassword) {
errors.confirmPassword = "Passwords do not match";
}
// Show errors
if (Object.keys(errors).length > 0) {
Object.entries(errors).forEach(([field, message]) => {
const errorElement = document.getElementById(`${field}Error`);
if (errorElement) {
errorElement.textContent = message;
}
});
return;
}
// Submit
try {
const response = await fetch("/api/register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error("Registration failed");
}
alert("Registration successful!");
registrationForm.reset();
} catch (error) {
alert(error.message);
}
});
Dynamic Form Fields
const form = document.getElementById("myForm");
const addFieldBtn = document.getElementById("addField");
let fieldCount = 1;
addFieldBtn.addEventListener("click", () => {
fieldCount++;
const fieldGroup = document.createElement("div");
fieldGroup.className = "field-group";
fieldGroup.innerHTML = `
<input type="text" name="field${fieldCount}" placeholder="Field ${fieldCount}">
<button type="button" class="remove-field">Remove</button>
`;
form.insertBefore(fieldGroup, addFieldBtn);
fieldGroup.querySelector(".remove-field").addEventListener("click", () => {
fieldGroup.remove();
});
});
File Upload
const fileInput = document.getElementById("fileInput");
const uploadBtn = document.getElementById("uploadBtn");
uploadBtn.addEventListener("click", async () => {
const file = fileInput.files[0];
if (!file) {
alert("Please select a file");
return;
}
// Validate file
const maxSize = 5 * 1024 * 1024; // 5MB
const allowedTypes = ["image/jpeg", "image/png", "application/pdf"];
if (file.size > maxSize) {
alert("File is too large");
return;
}
if (!allowedTypes.includes(file.type)) {
alert("Invalid file type");
return;
}
// Upload
const formData = new FormData();
formData.append("file", file);
try {
const response = await fetch("/api/upload", {
method: "POST",
body: formData
});
const result = await response.json();
console.log("Upload successful:", result);
} catch (error) {
console.error("Upload failed:", error);
}
});
Multi-Step Form
class MultiStepForm {
constructor(formId) {
this.form = document.getElementById(formId);
this.steps = this.form.querySelectorAll(".step");
this.currentStep = 0;
this.setupEventListeners();
this.showStep(0);
}
setupEventListeners() {
const nextBtns = this.form.querySelectorAll(".next-btn");
const prevBtns = this.form.querySelectorAll(".prev-btn");
nextBtns.forEach(btn => {
btn.addEventListener("click", () => this.nextStep());
});
prevBtns.forEach(btn => {
btn.addEventListener("click", () => this.prevStep());
});
this.form.addEventListener("submit", (e) => this.handleSubmit(e));
}
showStep(n) {
this.steps.forEach(step => step.style.display = "none");
this.steps[n].style.display = "block";
}
nextStep() {
if (this.currentStep < this.steps.length - 1) {
this.currentStep++;
this.showStep(this.currentStep);
}
}
prevStep() {
if (this.currentStep > 0) {
this.currentStep--;
this.showStep(this.currentStep);
}
}
handleSubmit(event) {
event.preventDefault();
const formData = new FormData(this.form);
const data = Object.fromEntries(formData);
console.log("Form submitted:", data);
}
}
// Usage
const multiStepForm = new MultiStepForm("myForm");
Form State Management
class FormManager {
constructor(formId) {
this.form = document.getElementById(formId);
this.initialState = this.getFormState();
}
getFormState() {
const formData = new FormData(this.form);
return Object.fromEntries(formData);
}
setFormState(state) {
Object.entries(state).forEach(([key, value]) => {
const field = this.form.elements[key];
if (field) {
field.value = value;
}
});
}
resetForm() {
this.setFormState(this.initialState);
}
isDirty() {
return JSON.stringify(this.getFormState()) !==
JSON.stringify(this.initialState);
}
saveToLocalStorage(key) {
localStorage.setItem(key, JSON.stringify(this.getFormState()));
}
loadFromLocalStorage(key) {
const saved = localStorage.getItem(key);
if (saved) {
this.setFormState(JSON.parse(saved));
}
}
}
// Usage
const formManager = new FormManager("myForm");
// Auto-save to localStorage
document.getElementById("myForm").addEventListener("change", () => {
formManager.saveToLocalStorage("formData");
});
// Load on page load
window.addEventListener("load", () => {
formManager.loadFromLocalStorage("formData");
});
Best Practices
Validate on Both Client and Server
// Client-side validation
function validateEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
// Always validate on server too!
// Never trust client-side validation alone
Provide Clear Error Messages
// Good - specific error message
if (password.length < 8) {
error.textContent = "Password must be at least 8 characters";
}
// Avoid - vague error message
if (!isValid) {
error.textContent = "Invalid input";
}
Disable Submit During Processing
form.addEventListener("submit", async (event) => {
event.preventDefault();
const submitBtn = form.querySelector("button[type='submit']");
submitBtn.disabled = true;
submitBtn.textContent = "Submitting...";
try {
// Submit form
} finally {
submitBtn.disabled = false;
submitBtn.textContent = "Submit";
}
});
Summary
- Form events: submit, input, change, focus, blur
- FormData: collect and send form data
- Validation: HTML5 and JavaScript validation
- Real-time validation: validate as user types
- Error handling: show clear error messages
- File upload: validate and upload files
- Multi-step forms: break forms into steps
- Best practice: validate on both client and server
Related Resources
Official Documentation
Next Steps
- Input Validation and Sanitization
- Event Delegation and Event Propagation
- Modifying DOM: Creating, Updating, Deleting Elements
Comments