FormData API & requestSubmit
Intercept, inspect, and manipulate HTML form submissions using the FormData API and trigger programmatic submissions with requestSubmit.
Overview
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.
Completed: 0 / 5 exercises
Knowledge Check
Question 1 of 4