Tutorial

Creating Custom Forms Using the JavaScript FormData API

Published on February 6, 2020
author

Jack Misteli

Creating Custom Forms Using the JavaScript FormData API

Building a form is easy to do as long as you don’t have an edge case. Then the bacon fat goes down the drain and your pipes are ruined. So you sometimes need some extra tools in your toolbelt to deal with it. The FormData API can be one of your tools.

The Core FormData API

FormData has a lot of features but the only method that works across all browsers is append. Let’s say we want to create a social application for people to share their bacon pictures. Here we’ll create a form that allows users to send a picture with a title and the author’s name. Our HTML markup will look like this:

<input type="text" name="author"  id="author-input" />
<input type="text" name="title" id="title-input" />
<input type="file" name="picture" id="picture-input" />
<button id="submit-button">SUBMIT</button>

To handle our data we can create the following code:

Module: bacon-form.js
const inputs = document.getElementsByTagName('input');
// This object will keep track of the changes of inputs
const applicationState = {
  title: "",
  author: "",
  picture: ""
}

document.getElementById('submit-button').onclick = async () => {
  // We create a new form object
  const form = new FormData();
  // we append each element to the form
  form.append('title', applicationState.title);
  form.append('author', applicationState.author);
  form.append('picture', applicationState.picture);

  const res = await fetch('https://postman-echo.com/post', {
    method: 'POST',
    mode: 'no-cors',
    body: form
  });
  // ... Do something with the response
}

// The rest of this code is functional
// It is not directly related to FormData

// This for loop reflects input changes to the application's state
for (let i = 0; i < inputs.length; i++) {
  const input = inputs[i]
  const inputName = input.name

  input.onchange = (e) => {
    let value = e.target.value
    // updating the application state according to the input the user interacted with
    if (inputName === 'picture' && e.target.files[0]) {
      setPicture(e.target.files[0]);
    } else {
      applicationState[inputName] = value;
    }
  };
}
// setPicture takes a file reads it as a base64URL and assigns that value to application.picture
const setPicture = (file) => {
  const fr = new FileReader();
  // Reading the data and encoding it as base64 to send it to the server
  fr.readAsDataURL(file);
  // When the data is done loading we assign it to picture
  fr.onloadend = (fileData) => {
    applicationState.picture = fileData.target.result;
  }
}

If this is our input:

Input sample with author name Jack Misteli Picture Name Alligator Bacon and a picture file

Then we press the submit button we’ll roughly get the following request headers:

{
  "Accept-Encoding": "gzip, deflate, br",
  "Connection": "keep-alive",
  "Content-Length": "4369",
  "Content-Type": "multipart/form-data",
  "Host": "postman-echo.com",
  "Origin": "null",
  "Sec-Fetch-Mode": "no-cors",
  "Sec-Fetch-Site": "cross-site"
}

And the following body:

{
  "title": "Alligator Bacon",
  "author": "Jack Misteli",
  "picture": "data:text/javascript;base64,iVBORw0KGgoAA......."
}

Please note that FormData constructor can take form data as an argument. So you could do:

Module: regular-form.html
<form id="user-form">
  <input type="text" name="username">
  <input type="password" name="password">
  <input type="file" name="picture" id="picture-input"/>
  <input type="submit">
</form>
<script>
  document.getElementById('user-form').onsubmit = async function (e) {
    e.preventDefault();
    // here `this` is the user-form HTML element
    const form = new FormData(this);
    ///... send form to server
  }
</script>

Another important gotcha, is that append does not overwrite a key if it already exists.

Module: double-bacon-form.js
const form = new FormData();
form.append('baconType', 'pork');
form.append('baconType', 'vegan');
// When you send your form it will look like this:
// {
//  baconType: pork
//  baconType: vegan
//}

If you want to overwrite a key value you will have to use other functions.

Advanced Forms

The FormData constructor and the append method are available in all browsers. Most of the other methods are pretty self-descriptive:

  • FormData.has(key): Checks if the key exists in the form.
  • FormData.set(key, value): Changes the value associated to the key.
  • FormData.delete(key): Deletes the entry associated with the key.
  • FormData.get(key): Accesses the first value associated with the key.
  • FormData.getAll(key): Creates an array of all the values associated with a key.
  • FormData.keys(), FormData.values(), FormData.entries(): Iterators used to get all the keys, associated values or just entries of the FormData.

🥓 That’s it if you have any questions you can fire them up on Twitter with a link to the article and I’ll do my best to answer them!

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar
Jack Misteli

author

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Become a contributor for community

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

DigitalOcean Documentation

Full documentation for every DigitalOcean product.

Resources for startups and SMBs

The Wave has everything you need to know about building a business, from raising funding to marketing your product.

Get our newsletter

Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

New accounts only. By submitting your email you agree to our Privacy Policy

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.