Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 445
Simone Avatar asked Jun 21 '15 12:06

Simone


People also ask

How can we collect data from form in HTML?

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 do I retrieve data from a form?

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.


1 Answers

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)); 
like image 77
zzzzBov Avatar answered Sep 28 '22 10:09

zzzzBov