How to get a key/value data set from a HTML form

46,028

Solution 1

When I originally wrote this answer FormData was not widely supported (and this was called out explicitly in the question). Now that it's been 6 years, FormData has excellent cross-browser support.

Because of this, I highly recommend using FormData directly to access data or serialize data to the server.

Jake Archibald has an excellent post describing FormData (and URLSearchParams) in depth, which I won't attempt to reproduce here, however I will include a few snippets that are relevant:

You can populate FormData state directly:

const formData = new FormData();
formData.set('foo', 'bar');
formData.set('hello', 'world');

...you can read an HTML form directly as FormData:

const formElement = document.querySelector('form');
const formData = new FormData(formElement);
console.log(formData.get('username'));

...you can use FormData directly as a fetch body:

const formData = new FormData();
formData.set('foo', 'bar');
formData.set('hello', 'world');

fetch(url, {
  method: 'POST',
  body: formData,
});

I recommend reading Jake's post in full and using the APIs that come with the browser over adding more code to build a less-resilient version of the same thing.


My original post preserved for posterity's sake:

Without a strong definition of what should happen with edge cases, and what level of browser support is required, it's difficult to give a single perfect answer to the question.

There are a lot of form behaviors that are easy to miss, which is why I recommend using a well-maintained function from a library, such as jQuery's serializeArray():

$('form').serializeArray();

I understand that there's a big push recently to move away from needlessly including jQuery, so for those who are interested in a vanilla JS solution serializeArray just won't do.

The next difficulty comes from determining what level of browser support is required. HTMLFormElement.elements significantly simplifies a serialization implementation, and selecting the form-associated elements without it is quite a pain.

Consider:

<form id="example">...</form>
<input type="text" form="example" name="lorem" value="ipsum"/>

A conforming implementation needs to include the input element. I will assume that I can use it, and leave polyfilling it as an exercise to the reader.

After that it'd unclear how <input type="file"/> should be supported. I'm not keen on needlessly serializing file elements into a string, so I've made the assumption that the serialization will be of the input's name and value, even though the value is practically useless.

Lastly, an input structure of:

{
    'input name': 'value',
    'textarea name': 'value'
}

Is excessively naive as it doesn't account for <select multiple> elements, or cases where two inputs have the same name. I've made the assumption that the input would be better as:

[
    {
        name: 'input name',
        value: 'value'
    },
    {
        name: 'textarea name',
        value: 'value'
    }
]

...and again leave transforming this into a different structure as an exercise for the reader.


Give me teh codez already!

var serialize = (function (slice) {
    return function (form) {
        //no form, no serialization
        if (form == null)
            return null;

        //get the form elements and convert to an array
        return slice.call(form.elements)
            .filter(function (element) {
                //remove disabled elements
                return !element.disabled;
            }).filter(function (element) {
                //remove unchecked checkboxes and radio buttons
                return !/^input$/i.test(element.tagName) || !/^(?:checkbox|radio)$/i.test(element.type) || element.checked;
            }).filter(function (element) {
                //remove <select multiple> elements with no values selected
                return !/^select$/i.test(element.tagName) || element.selectedOptions.length > 0;
            }).map(function (element) {
                switch (element.tagName.toLowerCase()) {
                    case 'checkbox':
                    case 'radio':
                        return {
                            name: element.name,
                            value: element.value === null ? 'on' : element.value
                        };
                    case 'select':
                        if (element.multiple) {
                            return {
                                name: element.name,
                                value: slice.call(element.selectedOptions)
                                    .map(function (option) {
                                        return option.value;
                                    })
                            };
                        }
                        return {
                            name: element.name,
                            value: element.value
                        };
                    default:
                        return {
                            name: element.name,
                            value: element.value || ''
                        };
                }
            });
    }
}(Array.prototype.slice));

Solution 2

You could go for a manual way (Below code is not tested though);

var form = document.getElementsByTagName("form");
var inputs = form[0].getElementsByTagName("input");

var formData = {};
for(var i=0; i< inputs.length; i++){
   formData[inputs[i].name] = inputs[i].value;
}
var formdata = JSON.stringify(formData);

if you use a library this would be easier. Eg:- in jQuery:

 var formObjects = $("form :input");
 formObjects.each(
function(){
formData[$(this).attr("name")] = $(this).val(); /*setting up name/value pairs */ 

 }
  );

in this code formObjects contains all input, select and textarea and other form element tags. so we don't need to manually search for each like in plain JS. Except for radio buttons (As @enhzflep implied this doesn't work correct for input[type=radio] )

But if you use jQuery you can directly use jQuery's serialize() function to grab the whole form by name-value pairs.

var url_friendly_name_value_string = $("form").serialize();

Solution 3

If you need object of {key: value, ...}

const form = document.getElementById("your-form")

const formEntries = new FormData(form).entries();
const formData = Object.assign(...Array.from(formEntries, ([name, value]) => ({[name]: value})));

console.log(formData)

Solution 4

You would need to manually generate the json/javascript object before sending it to the server. Should be a method called onSubmit which would pick each of the form's input value and create an object which can then be sent to the server page.

You can refer this answer which is similar to what you are looking for: Convert form data to JavaScript object with jQuery

Solution 5

Just use jQuery.

HTML:

<form id="my-form">
  <input type="text" name="my-field" />
</form>

JS:

var data = $('form#my-form').serializeArray(); // [{'my-field': 'value'}]
$.ajax({
  data: data
  //other config goes here
});
Share:
46,028

Related videos on Youtube

Simone
Author by

Simone

I'm a software engineer currently based in London, UK, originally from Italy. I was a kid when I built my first website. I learned pascal and vb6 in high school, then I moved on to web development with javascript and php. Eventually I discovered ruby and developed an interest in functional languages such as haskell and clojure. Other languages I've been using are c#, java, lua, elixir and elm. Nowadays I'm interested in building concurrent, distributed, fault-tolerant systems using the best tools available. github.com/simonewebdesign twitter.com/simonewebdesign linkedin.com/in/simonewebdesign

Updated on July 09, 2022

Comments

  • Simone
    Simone almost 2 years

    I'm simply looking for a way to get all the values from a <form>.

    I searched the Web for a while, stumbling across FormData, which seems quite what I'm looking for.

    However its API is not available on any browser, so I need an alternative.


    What I need in my specific case is an object of key/value pairs. For example:

    <form>
      <input type="text" name="firstname" value="John" />
      <input type="text" name="surname" value="doe" />
      <input type="email" name="email" value="" />
      <input type="radio" name="gender" value="male" />
      <input type="radio" name="gender" value="female" />
    </form>
    

    The object should be:

    {
      firstname: "John",
      surname: "doe",
      email: "",
      gender: ""
    }
    

    Edit: The above is just an example, it should work not only with <input> but also with the other tags (e.g. <select>, <textarea> and so on... even <input type="file"> should be supported).

    • zzzzBov
      zzzzBov almost 9 years
      when you say "even <input type="file"> should be supported" what do you expect as output? Are you expecting the entire contents of a potentially massive file to be serialized as a string? Are you expecting a reference object that can be used to read the file? Are you expecting the actual value of the field (which is just a sanitized name of the file)?
    • zzzzBov
      zzzzBov almost 9 years
      also, your expected output fails to account for multiple form elements with the same name or what happens when you use <select multiple>.
    • antoine129
      antoine129 almost 9 years
      would you accept to add some extra HTML like method and action attributes to your form and an invisible iframe?
  • enhzflep
    enhzflep almost 9 years
    For what it's worth - this wont work with radio-buttons.
  • Dan Jay
    Dan Jay almost 9 years
    @enhzflep : ya we'd have to look manually in plain javascript. in jQuery we can get all form data by ":input" selector.
  • Simone
    Simone almost 9 years
    Thanks, I had a look at serializeArray() but I'd rather avoid using jQuery
  • natnai
    natnai almost 9 years
    There is no reason not to use jQuery in this case, since it doesn't cause any performance issues - unless you really enjoy writing pointless boilerplate code in vanilla JS.
  • zzzzBov
    zzzzBov almost 9 years
    If you're using jQuery, why aren't you just using $('form').serializeArray()?
  • Raj
    Raj almost 9 years
    @zzzzBov : its good to use $('form').serializeArray() if it fits to the requirement. But the output differs. I found serializeArray() a little bit complicate while you have multiple select and checkboxes. It repeats the key with all different select values. For example if you have checkboxes than ouput will be. [ {checkbox: "checkbox1", checkbox: "checkbox2" }] . But the custom code I wrote, will produce [{checkbox:["checkbox1","checkbox2"]}] . This custom code minimizes the output and will be much easier to trackdown and validate if anybody wish to. Both are goode but choice depends
  • Etheryte
    Etheryte almost 9 years
    @user3497023 A rough 90 KB of code, of which you don't use 99% isn't exactly "no reason not to use".
  • natnai
    natnai almost 9 years
    It's 90kb. Even on a mobile that takes...1 second?
  • jonny
    jonny almost 9 years
    This is the most elegant solution +1
  • MatthewLee
    MatthewLee over 2 years
    perhaps a minor point, but this actually does not give you the data in the form [{'my-field': 'value'}], it gives it to you in the form { name: 'my-field', value: 'value' } which may or may not work for your use case.