Working with FormData in JavaScript

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:

FormData entries

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 or event.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!

Valentino Gagliardi

Hi! I'm Valentino! I'm a freelance consultant with a wealth of experience in the IT industry. I spent the last years as a frontend consultant, providing advice and help, coaching and training on JavaScript, testing, and software development. Let's get in touch!