Skip to main content
โšก Calmops

Form Handling and Validation

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

Official Documentation

Next Steps

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

Comments