🚀AdvancedAdvanced Forms

FormData API & requestSubmit

Intercept, inspect, and manipulate HTML form submissions using the FormData API and trigger programmatic submissions with requestSubmit.

Overview

The FormData API provides a programmatic way to construct, read, and modify the key-value pairs of an HTML form submission including file inputs. It mirrors exactly what the browser would send when a form is submitted, making it ideal for AJAX form handling. Creating a `new FormData(formElement)` automatically captures all named fields in their current state. You can then iterate, append, delete, or replace entries before sending via `fetch`. The `formdata` event fires before submission and gives you a chance to modify the FormData object at the point of collection. `requestSubmit()` is the correct programmatic way to submit a form unlike `form.submit()`, it runs the browser's native validation and fires the `submit` event, keeping your form handlers in the loop.

Key Points

  • •`new FormData(form)` captures all form fields (including files) as a key-value map
  • •`formData.get(name)`, `getAll(name)`, `set(name, value)`, `append(name, value)`, `delete(name)` are the core methods
  • •`formData.entries()` returns an iterator of `[name, value]` pairs suitable for `for...of`
  • •The `formdata` event fires on the form element during submission modify entries via `event.formData`
  • •`form.requestSubmit()` triggers validation and fires the submit event; `form.submit()` does neither
  • •`form.requestSubmit(submitter)` simulates clicking a specific submit button (sets `submitter` and `formaction`)
  • •Sending FormData with `fetch` automatically sets the correct `multipart/form-data` content type

Examples

Reading a form with FormData and submitting via fetch

<form id="profile-form">
  <input name="username" type="text" value="alice">
  <input name="email" type="email" value="alice@example.com">
  <input name="avatar" type="file" accept="image/*">
  <button type="submit">Save Profile</button>
</form>

<script>
document.getElementById('profile-form').addEventListener('submit', async (event) => {
  event.preventDefault();

  const formData = new FormData(event.target);

  // Read individual fields
  console.log(formData.get('username')); // "alice"
  console.log(formData.get('email'));    // "alice@example.com"

  // Log all fields
  for (const [name, value] of formData.entries()) {
    console.log(name, value);
  }

  // Send to server (browser sets multipart/form-data automatically)
  const response = await fetch('/api/profile', {
    method: 'POST',
    body: formData,
  });
});
</script>

Never set Content-Type manually when sending FormData let the browser set the boundary automatically.

The formdata event for intercepting and modifying submission data

<form id="order-form">
  <input name="product" value="widget">
  <input name="quantity" type="number" value="2">
  <button type="submit">Order</button>
</form>

<script>
const form = document.getElementById('order-form');

// Fires right before the FormData is finalized
form.addEventListener('formdata', (event) => {
  const fd = event.formData;

  // Add computed fields invisibly
  fd.append('timestamp', new Date().toISOString());
  fd.append('total_price', Number(fd.get('quantity')) * 9.99);

  // Remove a field if needed
  // fd.delete('debug_info');
});

form.addEventListener('submit', async (e) => {
  e.preventDefault();
  const fd = new FormData(form); // triggers the formdata event above
  await fetch('/api/order', { method: 'POST', body: fd });
});
</script>

requestSubmit vs form.submit()

<form id="search-form">
  <input name="q" type="search" required minlength="2">
  <button type="submit" id="normal-submit">Search</button>
  <button type="button" id="js-submit">JS Submit</button>
  <button type="button" id="js-request-submit">requestSubmit</button>
</form>

<script>
const form = document.getElementById('search-form');

form.addEventListener('submit', (e) => {
  e.preventDefault();
  console.log('Submit event fired, validation passed!');
});

// form.submit() skips validation AND skips the submit event handler
document.getElementById('js-submit').addEventListener('click', () => {
  form.submit(); // DANGEROUS: bypasses everything
});

// form.requestSubmit() runs validation, fires submit event
document.getElementById('js-request-submit').addEventListener('click', () => {
  form.requestSubmit(); // CORRECT: behaves like a real button click
});
</script>

Always use requestSubmit() for programmatic form submission. form.submit() is a legacy escape hatch.

Common Mistakes

  • Manually setting `Content-Type: multipart/form-data` when using fetch with FormData this breaks the boundary string; leave it unset
  • Using `form.submit()` instead of `form.requestSubmit()` submit() bypasses validation and your submit event handler
  • Calling `new FormData(form)` outside a submit handler for a form with file inputs files are only captured at submission time in some environments

Note

To convert FormData to a plain object, use `Object.fromEntries(formData)`. Note this loses multi-value fields (checkboxes with same name) use `formData.getAll(name)` for those.

Exercises

Write HTML to solve each exercise. Use Preview to see it render in a live iframe, then mark as done.

Loading editor…

Completed: 0 / 5 exercises

Knowledge Check

Question 1 of 4

What does `new FormData(formElement)` capture?