Skip to main content

Form Handling and Validation

Created: December 18, 2025 6 min read

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

Official Documentation

Next Steps

  1. Input Validation and Sanitization
  2. Event Delegation and Event Propagation
  3. Modifying DOM: Creating, Updating, Deleting Elements

Resources

Comments

Share this article

Scan to read on mobile