Working with FormData in JavaScript
Use case: how can I grab all the fields of an HTML form at once with JavaScript?
Consider a simple HTML form for saving tasks in a to-do list:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HTML forms and JavaScript FormData</title>
</head>
<body>
<form>
<label for="name">Name</label>
<input type="text" id="name" name="name" required>
<label for="description">Short description</label>
<input type="text" id="description" name="description" required>
<label for="task">Task</label>
<textarea id="task" name="task" required></textarea>
<button type="submit">Submit</button>
</form>
</body>
<script src="form.js"></script>
</html>
In this form we have:
- a text input for the name
- a text input for the description
- a textarea for the task
- a submit button
Each field has the appropriate type
, id
and name
attributes, plus the related label. How can we grab all the data from this form once the user clicks the submit button?
There are two ways: one is like a dirty hack, the other is cleaner, and most important idiomatic.
To follow along, create a file named form.js
in the same folder as the HTML form. Let's see.
Grabbing form fields from the event target
First of all we register an event listener for the submit
event on the form to stop the default behaviour (they send data to the back-end).
Then, to access the form field we use either this.elements
or event.target.elements
:
const form = document.forms[0];
form.addEventListener("submit", function(event) {
event.preventDefault();
const { name, description, task } = this.elements;
// or
// const { name, description, task } = event.target.elements;
console.log(name.value, description.value, task.value);
});
Here we destructure the three fields from this.elements
. If we know the fields beforehand this method is acceptable.
If instead there's a chance that more fields will be added dynamically in response to some user interaction, then we need to use FormData
.
Let's see how.
Going idiomatic with FormData
First of all we register an event listener for the submit
event on the form to stop the default behaviour.
Then, we build a FormData
object from the form:
const form = document.forms[0];
form.addEventListener("submit", function(event) {
event.preventDefault();
const formData = new FormData(this);
// do stuff
});
In addition to append()
, delete()
, get()
, set()
, FormData
also implement Symbol.iterator
. This means it's iterable with for...of
:
const form = document.forms[0];
form.addEventListener("submit", function(event) {
event.preventDefault();
const formData = new FormData(this);
for (const formElement of formData) {
console.log(formElement);
}
});
Each iteration step gives you an array of entries, where each array has in order:
- the name attribute of the form field
- the value of the input
Here's an example:
In addition to the aforementioned methods, the entries()
method offer the possibility to obtain an array of entries:
const form = document.forms[0];
form.addEventListener("submit", function(event) {
event.preventDefault();
const formData = new FormData(this);
const entries = formData.entries(); // array of entries
});
This plays well with Object.fromEntries()
(ECMAScript 2019):
const form = document.forms[0];
form.addEventListener("submit", function(event) {
event.preventDefault();
const formData = new FormData(this);
const entries = formData.entries();
const data = Object.fromEntries(entries);
});
Why is this useful? See by yourself:
const form = document.forms[0];
form.addEventListener("submit", function(event) {
event.preventDefault();
const formData = new FormData(this);
const entries = formData.entries();
const data = Object.fromEntries(entries);
// send out to a REST API
fetch("https://some.endpoint.dev", {
method: "POST",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json"
}
})
.then(/**/)
.catch(/**/);
});
Once you have the object you can send out the payload with Fetch.
Pitfall: omitting the name
attribute on form fields. Form fields without the appropriate name
attribute don't show up in the resulting FormData
object.
Conclusion
To grab all the fields from an HTML form you can use:
this.elements
orevent.target.elements
only if you know all the fields beforehand, and if they stay stable.FormData
to build an object with all the fields, which you can later transform, update, or send out to a remote API.
Newest versions of all the major browser are about to support also the new formdata event.
Thanks for reading!