15

I have form:

<form onchange="allvaluestostring()">
    <select name="status">
         <option value="*">All</option>
         <option value="1">Active</option>
         <option value="0">Inactive</option>
    </select>
    <select name="size">
          <option value="*">All</option>
          <option value="small">Small</option>
          <option value="big">Big</option>
    </select>
</form>

And the onchange action of any input in form I need to get a JavaScript string, for example "status=1&size=big" for using in httprequest.

Does there exist something in JavaScript that will take all form values when one of form inputs will be changed?

I used <select name="status" onchange="showExporteditems(this.name,this.value)">, but this will take only one input value for using only "status=1", but I need on each onchage all values from all inputs for string like "status=1&size=big&...etc....".

Without using jQuery.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Martin
  • 2,575
  • 6
  • 32
  • 53

11 Answers11

30

For input fields you could use ECMAScript 6 like the following:

Get your form in a constant:

const form = document.querySelector('form')

Grab all values:

Object.values(form).reduce((obj,field) => { obj[field.name] = field.value; return obj }, {})

The above snippet will produce an object with the input name as the key and its value.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Shairon Toledo
  • 2,024
  • 16
  • 18
  • 4
    This solution looks beautiful, but still has some issues: 1) There is still a big user base with browsers that don't support Object.values yet 2) the result includes values that are not form inputs. You could use `Object.values(form.elements)` to solve this. Would upvote if it had this fix and was based on Object.keys() instead :) – opyh Jan 26 '18 at 13:17
  • 2
    @opyh you may want to use babel when coding es6 you know – Teoman Tıngır Nov 15 '19 at 16:49
16

Here is a working fiddle in vanilla JavaScript, but you need to add a serialize utility function. This works exactly like $('form').serialize() in jQuery.

JavaScript:

var data;

function serialize(form) {
    if (!form || form.nodeName !== "FORM") {
        return;
    }
    var i, j, q = [];
    for (i = form.elements.length - 1; i >= 0; i = i - 1) {
        if (form.elements[i].name === "") {
            continue;
        }
        switch (form.elements[i].nodeName) {
        case 'INPUT':
            switch (form.elements[i].type) {
            case 'text':
            case 'hidden':
            case 'password':
            case 'button':
            case 'reset':
            case 'submit':
                q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
                break;
            case 'checkbox':
            case 'radio':
                if (form.elements[i].checked) {
                    q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
                }
                break;
            }
            break;
        case 'file':
            break;
        case 'TEXTAREA':
            q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
            break;
        case 'SELECT':
            switch (form.elements[i].type) {
            case 'select-one':
                q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
                break;
            case 'select-multiple':
                for (j = form.elements[i].options.length - 1; j >= 0; j = j - 1) {
                    if (form.elements[i].options[j].selected) {
                        q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].options[j].value));
                    }
                }
                break;
            }
            break;
        case 'BUTTON':
            switch (form.elements[i].type) {
            case 'reset':
            case 'submit':
            case 'button':
                q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
                break;
            }
            break;
        }
    }
    data = q.join("&");
}

And change your form onchange to

<form onchange="serialize(this)">

I tested it and am getting "size=small&status=0" in the console.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Seth
  • 10,198
  • 10
  • 45
  • 68
  • 1
    Put the function itself to the code, excelent solution! – Martin Apr 17 '14 at 18:01
  • This code is broken. having 3 – thenetimp Oct 28 '17 at 22:13
  • I can confirm that this is unfortunately broken - I would refactor it to not work with Switch Statements or else use better defaults, as it's far too easy to leave out input types like input[type="tel"]. – Bashu Naimi-Roy Jul 14 '20 at 03:03
13

I guess this would be a nice ECMAScript 6 vanilla JavaScript-based solution.

const form = document.getElementById("sampleForm");

form.addEventListener("submit", e => {
    e.preventDefault();
    let reqBody = {};
    Object.keys(form.elements).forEach(key => {
        let element = form.elements[key];
        if (element.type !== "submit") {
            reqBody[element.name] = element.value;
        }
    });
    submitForm(reqBody); // Call to function for form submission
});
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Subhasis Das
  • 294
  • 5
  • 15
5

this would likely be used when you attach an event to the form itself. I use the 'submit' event. Then this refers to the form itself.

2022 version

const formData = new URLSearchParams(new FormData(this));
Goddard
  • 2,863
  • 31
  • 37
2

I don't know if there is an easier wait to do this but this solution, using just ECMAScript, is working for me:

function getPlainObjectFromFormElement(form) {
    const elements = form.elements;
    return Object.keys(elements)
        .reduce((obj, field) => {
            if (isNaN(field)) {
                obj[field] = elements[field].value;
            }
            return obj;
        }, {});
}

document.querySelector("form").onsubmit = function (e) {
    alert(JSON.stringify(getPlainObjectFromFormElement(e.target)));
}
dave008
  • 402
  • 3
  • 9
2

As of late 2022, there is a better way to do this now, and it's supported by all modern browsers. Use the FormData constructor.

tldr;

console.log(Object.fromEntries(new FormData(form)));
// or
console.log(Object.fromEntries(new FormData(e.target)));

Example

Here is a full example of how to use the FormData constructor to get data from a form. You can see a live example here.

<!DOCTYPE html>
<html>
  <head>
    <title>FormData Example</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <form id="form">
      <input type="text" name="a" value="foo" />
      <input type="text" name="b" value="bar" />
      <input type="text" name="c" value="baz" />
      <input type="checkbox" name="check" checked disabled />
      <button>Submit</button>
    </form>

    <script>
      const form = document.getElementById("form");

      form.addEventListener("submit", (e) => {
        // this is used only for this example, 
        // to prevent submission of the form
        // so you can inspect the data below
        e.preventDefault();

        // you can pass "form" or "e.target" to FormData
        console.log(Object.fromEntries(new FormData(e.target)));
        console.log(Object.fromEntries(new FormData(form)));
        // { a: "foo", b: "bar", c: "baz" }
      });
    </script>
  </body>
</html>
jonschlinkert
  • 10,872
  • 4
  • 43
  • 50
1

2021 version (TypeScript)

  const onSubmit = (e: any) => {
    e.preventDefault();
    const form = e.target as HTMLFormElement;
    const values = Object.fromEntries(
      Array.from(form.elements).map((x: Element) => {
        const input = x as HTMLInputElement;
        console.log(input);
        return [input.name ?? input.id, input.value];
      })
    );
    console.log(values);
  };
Slawa
  • 1,141
  • 15
  • 21
0

When you're using the method GET request type. You're already using the default method which would append the form data to the URL.

So, you'll get the select element's value appended along with the name.

However, if you want to dynamically change the URL. Then you can try it like:

function allvaluestostring() {
  var sts = document.getElementById("status").value;
  var size = document.getElementById("size").value;
  history.pushState("", "Title", "new_URL");
}

In the above statement, the values are stored in the variables and a history.pushState() method is called and it would change the string of the URL dynamically.

History API

Can I prevent history.popstate from triggering on initial page-load?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Afzaal Ahmad Zeeshan
  • 15,669
  • 12
  • 55
  • 103
  • Puropuse of form is to show results onchange by ajax, but on submit will do export selected data to the file. Export (when form is submited) is different function than showing results example. that means i dont need to change actual page url, but generate url for hthpprequest included page. – Martin Apr 17 '14 at 17:56
0

Dave's answer (https://stackoverflow.com/a/65383511/2740403) is perfect, but you need to extend it a little to work for multiselects...

    function getPlainObjectFromFormElement(form) {
    const elements = form.elements; 
    return Object.keys(elements).reduce((obj, field) => { 
        if (isNaN(field)) {

            // NEW SECTION
            if(elements[field].classList.contains("multiselect") ){ 
                var selected = [];
                for (var option of elements[field].options){
                    if (option.selected) {
                        selected.push(option.value);
                    }
                }
                obj[field] = selected.toString(); 
            // END NEW SECTION

            }else{
                obj[field] = elements[field].value;
            }
        }
        return obj;
    }, {});
}
Greggers
  • 131
  • 1
  • 6
0

2023 Version

(Since we're naming years here lol)

function onSubmit(e: Event) {

  const form = new FormData(e.target as HTMLFormElement);
  const { name, email } = Object.fromEntries(form);

  // do something with data

}

Here is an example with the Record type assuming a key / value both of type string:

const { name } = Object.fromEntries(form) as Record<string, string>;

J

Jonathan
  • 3,893
  • 5
  • 46
  • 77
  • This solution is quite elegant. But how to append new pairs of key/value in a such element. `form.append(k, v)` throws an error – Emilio Conte Jan 18 '23 at 14:28
  • It should work fine, you must have some other error - https://svelte.dev/repl/605e37fb97184f608a39190fb1f51087?version=3.55.1 – Jonathan Jan 18 '23 at 14:56
  • Indeed! it's just that formData looks like an empty dict and when I try to JSON stringify it, it returns an empty string. Seems that you can't add formData as body in a fetch method. Here, what I'm trying to do https://svelte.dev/repl/a3c5e2545d4d47c3922539194942716d?version=3.55.1 – Emilio Conte Jan 18 '23 at 15:35
  • I'm not sure what you're trying to do, but probably not appropriate for this question. I suggest you create a new question with a full example repo (not svelte) demonstrating your problem. – Jonathan Jan 20 '23 at 00:22
  • Thanks, you're right. I have a better understanding now of my mistakes now. – Emilio Conte Jan 20 '23 at 10:19
0

If you want all elements with certain class (example: step1):

var dataform = new FormData;
 var elemSteps = document.querySelectorAll('#frm-checkout .step1');
 elemSteps.forEach(elemStep => {
     dataform.append(elemStep.getAttribute('name'), elemStep.value)
 });
  var params = new URLSearchParams(dataform);
 params = params.toString();

//to get all data
var form = document.getElementById('frm-checkout');
 var dataform = new FormData(form); 
  var params = new URLSearchParams(dataform);
 params = params.toString();