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).
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.
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));