Forms & Validation

Collect and validate user input. The most common interactive pattern on the web.

Form Basics

Handle form submission with JavaScript.

// HTML:
// <form id="signup">
//   <input name="email" type="email" required>
//   <input name="password" type="password" required>
//   <button type="submit">Sign Up</button>
// </form>

const form = document.getElementById("signup");

form.addEventListener("submit", (e) => {
  e.preventDefault(); // stop page reload!
  
  // Get values:
  const email = form.elements.email.value;
  const password = form.elements.password.value;
  
  // Do something:
  console.log("Signing up:", email);
});

💡 Always call e.preventDefault() in the submit handler — otherwise the browser reloads the page (default form behavior).

Reading Values

Get data from different input types.

// Text, email, password, number:
input.value;          // string (always a string!)
Number(input.value);  // convert to number

// Checkbox:
checkbox.checked;     // true/false

// Radio buttons:
const selected = form.querySelector('input[name="color"]:checked');
selected?.value;      // value of selected radio

// Select (dropdown):
select.value;                    // selected option's value
select.selectedIndex;            // index of selected option
select.options[select.selectedIndex].text; // display text

// Multiple select:
const selected = [...select.selectedOptions].map(o => o.value);

// Textarea:
textarea.value;       // text content

// File input:
fileInput.files;      // FileList
fileInput.files[0];   // first File object

FormData API

Get all form values at once — the modern way.

form.addEventListener("submit", (e) => {
  e.preventDefault();
  
  // Grab ALL form values in one line:
  const formData = new FormData(form);
  
  // Read individual fields:
  formData.get("email");     // "user@test.com"
  formData.get("password");  // "secret123"
  
  // Convert to a plain object:
  const data = Object.fromEntries(formData);
  // { email: "user@test.com", password: "secret123" }
  
  // Send to API:
  fetch("/api/signup", {
    method: "POST",
    body: JSON.stringify(data),
    headers: { "Content-Type": "application/json" }
  });
});
// FormData methods:
formData.get("field");     // get value
formData.getAll("tags");   // get all values (multi-select)
formData.has("field");     // check if exists
formData.set("field", "new value"); // set/overwrite
formData.append("tags", "new");     // add another value
formData.delete("field");  // remove

// Iterate all entries:
for (const [key, value] of formData) {
  console.log(key, value);
}

// Send FormData directly (for file uploads):
fetch("/api/upload", {
  method: "POST",
  body: formData, // Content-Type set automatically!
});

Validation

Built-in HTML validation + Constraint Validation API.

// HTML validation attributes:
// <input type="email" required>
// <input type="number" min="0" max="100">
// <input minlength="8" maxlength="50">
// <input pattern="[A-Za-z]{3,}">

// Check validity with JavaScript:
const input = document.querySelector("input");

input.validity.valid;        // true/false (overall)
input.validity.valueMissing; // true if required + empty
input.validity.typeMismatch; // true if wrong type (email)
input.validity.tooShort;     // true if < minlength
input.validity.tooLong;      // true if > maxlength
input.validity.patternMismatch; // true if doesn't match pattern
input.validity.rangeUnderflow;  // true if < min
input.validity.rangeOverflow;   // true if > max

// Check entire form:
form.checkValidity();  // true if all fields valid
form.reportValidity(); // show validation UI

Custom Validation

Build your own validation logic.

// Custom validation messages:
const email = document.querySelector("#email");

email.addEventListener("input", () => {
  if (email.validity.typeMismatch) {
    email.setCustomValidity("Please enter a valid email");
  } else {
    email.setCustomValidity(""); // clear = valid
  }
});

// Full custom validation pattern:
form.addEventListener("submit", (e) => {
  e.preventDefault();
  const errors = validateForm(form);
  
  if (errors.length > 0) {
    showErrors(errors);
    return;
  }
  
  submitForm(form);
});

function validateForm(form) {
  const errors = [];
  const data = Object.fromEntries(new FormData(form));
  
  if (!data.email.includes("@")) {
    errors.push({ field: "email", message: "Invalid email" });
  }
  if (data.password.length < 8) {
    errors.push({ field: "password", message: "Min 8 characters" });
  }
  if (data.password !== data.confirmPassword) {
    errors.push({ field: "confirmPassword", message: "Passwords don't match" });
  }
  
  return errors;
}
// Real-time validation (as user types):
const passwordInput = document.querySelector("#password");
const feedback = document.querySelector("#password-feedback");

passwordInput.addEventListener("input", () => {
  const val = passwordInput.value;
  const checks = [
    { test: val.length >= 8, msg: "8+ characters" },
    { test: /[A-Z]/.test(val), msg: "Uppercase letter" },
    { test: /[0-9]/.test(val), msg: "Number" },
    { test: /[^A-Za-z0-9]/.test(val), msg: "Special character" },
  ];
  
  feedback.innerHTML = checks
    .map(c => `<span class="${c.test ? 'valid' : 'invalid'}">${c.msg}</span>`)
    .join("");
});

FAQ

Common questions about forms.