2

I am sending a form with both radio and checkbox inputs (so multiple elements have the same name) and this is what I've come up with.

Is there a faster/simpler way with pure JavaScript approach to turn an HTML form into a object so I can use JSON.stringify?

No frameworks or libraries.

function serialize_form(felement) {
    const names = new Set();
    let values = {}
    for(let i = 0; i < felement.elements.length; i++) {
        if(felement[i].name && (felement.name != undefined || felement.name != "")){
            names.add(felement[i].name)
        }
    }

    for(let name of names) {
        values[name] = felement[name].value;
    }

   console.log(names, values);

   return values;
}
John
  • 1
  • 13
  • 98
  • 177
David
  • 17,673
  • 10
  • 68
  • 97
  • 2
    See my answer on FormData here: https://stackoverflow.com/a/46376650/362536 – Brad Dec 14 '20 at 02:45
  • 2
    https://developer.mozilla.org/en-US/docs/Web/API/FormData – Brad Dec 14 '20 at 02:46
  • @brad I saw that question but never imagined a non-jquery answer to it. Thank you. – David Dec 14 '20 at 02:47
  • Combining FormData and this [answer](https://stackoverflow.com/a/46774073/9908) to how to serialize FormData is the answer to this question. – David Dec 14 '20 at 03:01

2 Answers2

2

You can do some simple transformations using FormData.keys() and FormData.getAll()

document.querySelector("form").addEventListener("submit", e => {
  e.preventDefault()
  
  const fd = new FormData(e.target)
  const obj = Object.fromEntries(Array.from(fd.keys(), key => {
    const val = fd.getAll(key)
    return [ key, val.length > 1 ? val : val.pop() ]
  }))
  
  document.querySelector("pre").innerHTML = JSON.stringify(obj, null, 2)
})
<form>
  <p><input type="text" name="text" value="text value"></p>
  <p>
    Checkboxes:
    <label>Foo <input type="checkbox" value="foo" name="checkbox"></label>
    <label>Bar <input type="checkbox" value="bar" name="checkbox"></label>
    <label>Baz <input type="checkbox" value="baz" name="checkbox"></label>
  </p>
  <p>
    Select:
    <select name="select" multiple>
      <option>foo</option>
      <option>bar</option>
      <option>baz</option>
    </select>
  </p>
  <p><button>Serialize</button></p>
</form>
<pre></pre>

Note that this changes values between arrays and strings based on how many selections are made. If you want a more consistent API, just use arrays for every value even if there's only one selection.

const obj = Object.fromEntries(Array.from(fd.keys(), key => 
  [ key, fd.getAll(key) ]))
Phil
  • 157,677
  • 23
  • 242
  • 245
0

Thanks to @brad pointing out FormData I was able to cut my serialize_form function down and that led to this answer https://stackoverflow.com/a/46774073/9908.

function serialize_form(felement) {
            const fd = new FormData(felement);
            let object = {};


            fd.forEach((value, key) => {
            // Reflect.has in favor of: object.hasOwnProperty(key)
                if(!Reflect.has(object, key)){
                    object[key] = value;
                    return;
                }
                if(!Array.isArray(object[key])){
                    object[key] = [object[key]];
                }
                object[key].push(value);
            });
            console.log(object);
            return object;
        }
codemonkey
  • 7,325
  • 5
  • 22
  • 36
David
  • 17,673
  • 10
  • 68
  • 97
  • 1
    How about `Object.fromEntries(new FormData(felement))` – Phil Dec 14 '20 at 03:06
  • @Phil I just saw that answer for the jQuery form question and I am testing right now if it can handle select's with multiple values. It would be nice if it is just a one liner. – David Dec 14 '20 at 03:09
  • 1
    Ah yeah, it fails on multi-select or repeated names – Phil Dec 14 '20 at 03:09