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).
The form-data can be sent as URL variables (with method="get" ) or as HTTP post transaction (with method="post" ). Notes on GET: Appends form-data into the URL in name/value pairs. The length of a URL is limited (about 3000 characters)
How to retrieve form data sent via GET. When you submit a form through the GET method, PHP provides a superglobal variable, called $_GET. PHP uses this $_GET variable to create an associative array with keys to access all the sent information ( form data ). The keys is created using the element's name attribute values.
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));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With