Inspired by this StackOverflow question.
The issue is that JSON.stringify()
does not handle FormData
well. However, it can handle arrays. Therefore we can use Array.from()
to convert FormData
into Array
:
const form = document.getElementById('someform'); const data = new FormData(form); const arr = Array.from(data); localStorage.setItem('myform', JSON.stringify(arr));
Array.from(data)
will produce something like this:
Deserialization of the array is pretty easy:
for (let i=0; i<arr.length; ++i) { const item = arr[i]; const el = form.elements[item[0]]; el.value = item[1]; }
However, there are a few gotchas:
- If
el.type === 'file'
, you cannot assign it any value other than''
: an attempt to do so will result in aSecurityError
exception. Thus, we first need to check the type of the field, and if it is a file, then assign an empty string to it. - If we have elements with the same name — like this:
<form id="form"> <input name="radio" type="radio" value="0"/> <input name="radio" type="radio" value="1"/> <input name="checkbox" type="checkbox" value="0"/> <input name="checkbox" type="checkbox" value="1"/> <input type="text" name="text"/> <input type="text" name="text" disabled="disabled"/> <input type="text" name="text"/> </form>
then
form.elements
will store them as RadioNodeList, like this:
FormData
skips disabled elements.
Taking the above into account, we can rewrite deserialization like this:
const map = {}; for (let item of a) { const name = item[0]; const el = form.elements[name]; if (el instanceof RadioNodeList) { if (el[0].type === 'radio') { el.value = item[1]; } else if (el[0].type === 'checkbox') { for (let j=0; j<el.length; ++j) { if (el[j].value == item[1]) { el[j].checked = true; break; } } } else { let idx; if (name in map) { idx = ++map[name]; } else { idx = map[name] = 0; } while (el[idx].disabled && idx < el.length) { ++idx; ++map[name]; } if (idx < el.length) { el[idx].value = el[idx].type === 'file' ? '' : item[1]; } } } else if (el.type === 'file') { el.value = ''; } else { el.value = item[1]; } }
This solution is still not an ideal one: for example, it is possible to break it by giving the same name to inputs of different types (does anyone do that?). However, it can give you a starting point in writing your own code.
Store FormData object in localStorage