0

I have a rather extensive form that I use to create a 3-4 page report. There are times when, for one reason or another, I can't complete and submit the form to generate the report. Is it possible to somehow save the values from the form fields to a file on the local device and then, at a later date, reload that data back into the form so that all the same values are checked/selected/loaded? I'm assuming I would need some type of Javascript button that loops through and reads if items are checked or not, but I'm unsure of how to save and load that information. A short example of the form might be:

<form method="POST" action="processnote.php">

    <legend>
        <h1>Academic Background</h1>
    </legend>
    <b>Highest Education Level</b>
    <ul class="checkbox">
        <li><input type="checkbox" name="education[]" id="education0" value="no formal education"><label for="education0">No Formal Education</label></li>
        <li><input type="checkbox" name="education[]" id="education1" value="some high school"><label for="education1">Some High School</label></li>
        <li><input type="checkbox" name="education[]" id="education2" value="high school/GED"><label for="education2">High School</label></li>
        <li><input type="checkbox" name="education[]" id="education3" value="some college"><label for="education3">Some College</label></li>
        <li><input type="checkbox" name="education[]" id="education4" value="trade school"><label for="education4">Trade School</label></li>
        <li><input type="checkbox" name="education[]" id="education5" value="an associate degree"><label for="education5">Associate's Degree</label></li>
        <li><input type="checkbox" name="education[]" id="education6" value="a bachelor degree"><label for="education6">Bachelor's Degree</label></li>
        <li><input type="checkbox" name="education[]" id="education7" value="a master degree"><label for="education7">Master's Degree</label></li>
        <li><input type="checkbox" name="education[]" id="education8" value="a doctoral degree"><label for="education8">Doctoral Degree</label></li>
        <li><label for="education9">Other:</label><input type="text" name="education[]" id="education9" value=""></li>
    </ul>

    <p>
        <input type="submit" class="btn btn-default btn-lg" name="submit" value="Submit">
        <input type="reset" class="btn btn-default btn-lg" value="Reset">
    </p>
</form>

So, if "High School" and "Some College" were selected and in the text field "certificate program" was typed, how could I save those values to a file and then reload those values back into a clear copy of the form? Is it possible? (I know almost anything is possible...but you know...reasonably possible for a novice)

Robert
  • 130
  • 1
  • 1
  • 9
  • Cookies or clipboard, JSON.strigify / parse or escape / unescape for encoding of complex objects in case ? https://www.w3schools.com/js/js_cookies.asp https://www.w3schools.com/howto/howto_js_copy_clipboard.asp – Jan Apr 17 '20 at 15:19
  • I don't think that's what I'm looking for. Ideally, there would be a button to export which then would prompt to download and allow you to give it a file name and then another button to import that would open up a dialog to allow you to select the file and import it back into the form. As I said, this form is very extensive. We're talking about 2000 lines of code here. – Robert Apr 17 '20 at 15:27
  • Also possible, thought you are creating local file behind scenes - security restrictions. Then you can generate Data URI for generated data download and use File input for loading... Suppose here an example for download https://stackoverflow.com/questions/56886672/how-to-export-geojson-in-javascript/56932101#56932101 – Jan Apr 17 '20 at 15:38
  • No, that's not correct. I don't want to create a local file behind the scenes. Just the opposite. I want to have it prompt to download so the user (me) can pick the name for the file and then, at a later time, reload that file and have all the previous selections that where saved be reselected back in the form! My form creates a psychological report. There are times...many in fact...where I need to save a work in progress, use the form on a different patient, and then the next day I might finish the first patient and I'd like to reload the work in progress back into the form for that patient. – Robert Apr 19 '20 at 18:03
  • Tom, here is an example of what I'm talking about: https://imgur.com/54XofvY - I would love for those buttons to be able to do what I'm talking about! :-) – Robert Apr 19 '20 at 18:12

2 Answers2

3

Quite complicated task, tested mostly in IE 11 ? Had some problems with FF during development, but now it seems to be working also in FF & Chrome, but not much tested there.

var myForm = document.forms[0],
  formData;
myForm.countries.selectedIndex = -1;
formData = getFormData(myForm[0]);

function saveForm(i) {
  var values = getFormData(i);

  // show JSON read
  document.getElementById("saving").innerText = JSON.stringify(values, null, 1)
    .replace(/\n "/g, '\n')
    .replace(/\n*[\{\}]\n*/g, '')
    .replace(/,\n/g, '\n')
    .replace(/([^"]+)":/g, '$1:');

  if (prompt('Clear data now ?', 'Yes will load them by file button') != null) setFormData(myForm, formData);

  return downloadObjectAsJson(values, "test");
}

function loadBack(t) {
  var f = t.files[0];

  var reader = new FileReader();
  // Closure to capture the file information.
  reader.onload = (function(theFile) {
    return function(e) {
      var json = JSON.parse(e.target.result);
      setFormData(myForm, json);
      return;
    }
  })(f);

  // Read in the image file as a data URL.
  reader.readAsText(f);
}

function setFormData(frm, values) {
  for (var v in values) {
    var el = frm[v],
      val = values[v];
    if (el.constructor == HTMLCollection) {
      // case "radio":
      //    values[el.name].checked = val;
      // break;
      for (var p = 0; p < el.length; p++) {
        el[p].checked = (el[p].value === val);
      }
    } else switch (el.type) {
      case "checkbox":
        el.checked = val;
        break;
      case 'select-one':
        if (val < 0) el.selectedIndex = val;
        else {
          for (var o = 0; o < el.options.length; o++) {
            if (el.options[o].value == val) el.selectedIndex = o;
          }
        }
        break;
      case 'select-multiple':
        for (var o = 0; o < el.options.length; o++) {
          el.options[o].selected = (val.indexOf(el.options[o].value) > -1);
        }
        break;
      default:
        el.value = val;
    }
  }
}

function getFormData(i) {
  var frm = i.form;
  var values = {};
  for (var a = 0; a < frm.length; a++) {
    var el = frm[a];
    switch (el.type) {
      case "checkbox":
        values[el.name] = el.checked;
        break;
      case "radio":
        if (el.checked) values[el.name] = el.value;
        else if (values[el.name] === undefined) values[el.name] = false;
        break;
      case 'select-one':
        values[el.name] = el.selectedIndex < 0 ? -1 : el.options[el.selectedIndex].value;
        break;
      case 'select-multiple':
        values[el.name] = [];
        for (var i = 0; i < el.options.length; i++) {
          if (el.options[i].selected) values[el.name].push(el.options[i].value);
        }
        break;
      case 'fieldset':
        break;
      case 'button':
        break;
      case 'submit':
        break;
      case 'reset':
        break;
      case 'file':
        break;
      case undefined:
        break;
      default:
        values[el.name] = el.value;
    }
  }
  return values;
}

function downloadObjectAsJson(exportObj, exportName) {
  if (navigator.msSaveBlob) { // IE10+
    var blob = new Blob([JSON.stringify(exportObj, null, 1)], {
      type: 'application/json'
    });
    return navigator.msSaveBlob(blob, exportName + ".json");
  }
  var dataStr = "data:application/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportObj, null, 1));
  var downloadAnchorNode = document.getElementsByName('_saveAnchor')[0];
  downloadAnchorNode.setAttribute("href", dataStr);
  downloadAnchorNode.setAttribute("download", exportName + ".json");
  downloadAnchorNode.click();
  downloadAnchorNode.remove();
}
<!-- form elements from https://www.fincher.org/tips/web/SimpleForm.shtml -->
<form>
   First Name: <input type="text" name="FirstName"/><br />
   <textarea name="comments"></textarea><br />
   Password:<input type="password" name="mypassword" value="start"/> „start“ as test value you can get from server<br />
   <input type="radio" name="title" value="mr" />Mr.<br />
   <input type="radio" name="title" value="ms" />Ms.<br />
   <input type="radio" name="title" value="decline"/>decline<br />
   <fieldset style="margin: 1em; text-align: center;">
      <legend>Select a Hero</legend>
      <input type="radio" name="hero"  value="Agamemnon" /> Agamemnon
      <input type="radio" name="hero"  value="Achilles" /> Achilles
      <input type="radio" name="hero"  value="Hector" /> Hector
      <div style="height: 1em;" />
   </fieldset>
   <input type="checkbox" name="mail" />Mail me more info<br />
   <input type="checkbox" name="e-mail" />E-Mail me more info<br />
      <select name="countries">
      <option>Argentina</option>
      <option>Albania</option>
      <option>Afganistain</option>
      <option>Aruba</option>
   </select>
   <select name="menuitems">
      <optgroup id="opt1" label="food">
         <option>Hamburgers</option>
         <option>Hotdogs</option>
      </optgroup>
      <optgroup id="opt2" label="drinks">
         <option>Coke</option>
         <option>Pepsi</option>
         <option>Water</option>
      </optgroup>
   </select>
   <select name="computerbrands" multiple="multiple">
      <option value="DELL">DELL</option>
      <option value="IBM">IBM</option>
      <option value="HP">HP</option>
      <option value="Compaq">Compaq</option>
      <option value="Sony">Sony</option>
   </select>
   <select name="countries2" size="4">
      <option value="0">Argentina</option>
      <option value="1">Albania</option>
      <option value="2">Afganistain</option>
      <option value="3">Aruba</option>
      <option value="4">Arabia</option>
   </select>
   <input type="hidden" name="computerType" value="hidden DELL" />
   <input type="submit" name="submitbutton" value="Submit" />
   <a name="_saveAnchor"><input type="button" value="Save content as JSON" onclick="saveForm(this)"></a>
   <input type="file" name="formFile" onchange="loadBack(this)"/>
</form>
<div id="saving"></div>

Made 2 new bookmarklets, only problem is to find proper form...

This 1 will save longest form to JSON

javascript:f=document.forms,longest=0;frm=0;for(i=0;i<f.length;i++)if(f[i].length>longest){longest=f[i].length;frm=f[i]}values={};for(a=0;a<longest;a++){el=frm[a];switch(el.type){case"checkbox":values[el.name]=el.checked;break;case"radio":if(el.checked)values[el.name]=el.value;else if(values[el.name]===undefined)values[el.name]=false;break;case'select-one':values[el.name]=el.selectedIndex<0?-1:el.options[el.selectedIndex].value;break;case'select-multiple':values[el.name]=[];for(i=0;i<el.options.length;i++){if(el.options[i].selected)values[el.name].push(el.options[i].value)}break;case'fieldset':break;case'button':break;case'submit':break;case'reset':break;case'file':break;case undefined:break;default:values[el.name] = el.value}}dataStr="data:application/json;charset=utf-8,"+encodeURIComponent(JSON.stringify(values));dwn=document.createElement('A');dwn.setAttribute("href",dataStr);dwn.setAttribute("download",location.href.split('/').pop()+".json");dwn.click();dwn.remove();undefined

Or formatted content

f = document.forms, longest = 0; frm = 0;
for (i = 0; i < f.length; i++)
    if (f[i].length > longest) { longest = f[i].length; frm = f[i] }
values = {};
for (a = 0; a < longest; a++) {
    el = frm[a];
    switch (el.type) {
        case "checkbox": values[el.name] = el.checked;
            break;
        case "radio":
            if (el.checked)
                values[el.name] = el.value;
            else if (values[el.name] === undefined)
                values[el.name] = false;
            break;
        case 'select-one': values[el.name] = el.selectedIndex < 0 ? -1 : el.options[el.selectedIndex].value; break;
        case 'select-multiple': values[el.name] = [];
            for (i = 0; i < el.options.length; i++) {
                if (el.options[i].selected)
                    values[el.name].push(el.options[i].value)
            }
            break;
        case 'fieldset': break;
        case 'button': break;
        case 'submit': break;
        case 'reset': break;
        case 'file': break;
        case undefined: break;
        default: values[el.name] = el.value
    }
} dataStr = "data:application/json;charset=utf-8," + encodeURIComponent(JSON.stringify(values));
dwn = document.createElement('A');
dwn.setAttribute("href", dataStr);
dwn.setAttribute("download", location.href.split('/').pop() + ".json");
dwn.click();
dwn.remove(); undefined

And this will read form from JSON

javascript:function setFormData(values){f=document.forms,longest=0;frm=0;for(i=0;i<f.length;i++)if(f[i].length>longest){longest=f[i].length;frm=f[i]}for(v in values){el=frm[v];val=values[v];if(el.constructor==HTMLCollection){for(p=0;p<el.length;p++){el[p].checked=(el[p].value===val)}}else switch(el.type){case"checkbox":el.checked=val;break;case'select-one':if(val<0)el.selectedIndex=val;else{for(o=0;o<el.options.length;o++){if(el.options[o].value==val)el.selectedIndex=o}}break;case'select-multiple':for(o=0;o<el.options.length;o++){el.options[o].selected=(val.indexOf(el.options[o].value)>-1)}break;default:el.value=val}}}f=document.createElement("input");f.type="file";f.onchange=(function(){reader=new FileReader();reader.onload=(function(theFile){return function(e){json = JSON.parse(e.target.result);setFormData(json);return;}})(event.srcElement.files[0]);reader.readAsText(event.srcElement.files[0]);});document.body.prepend(f);f.focus();

And this one:

function setFormData(values) {
    f = document.forms, longest = 0; frm = 0;
    for (i = 0; i < f.length; i++)
        if (f[i].length > longest) {
            longest = f[i].length; frm = f[i]
        }
    for (v in values) {
        el = frm[v]; val = values[v];
        if (el.constructor == HTMLCollection) {
            for (p = 0; p < el.length; p++) {
                el[p].checked = (el[p].value === val)
            }
        } else switch (el.type) {
            case "checkbox": el.checked = val;
                break;
            case 'select-one':
                if (val < 0) el.selectedIndex = val;
                else {
                    for (o = 0; o < el.options.length; o++) {
                        if (el.options[o].value == val) el.selectedIndex = o
                    }
                }
                break;
            case 'select-multiple':
                for (o = 0; o < el.options.length; o++) {
                    el.options[o].selected = (val.indexOf(el.options[o].value) > -1)
                }
                break;
            default: el.value = val
        }
    }
}
f = document.createElement("input");
f.type = "file";
f.onchange = (function () {
    reader = new FileReader();
    reader.onload = (function (theFile) {
        return function (e) {
            json = JSON.parse(e.target.result);
            setFormData(json);
            return;
        }
    })(event.srcElement.files[0]);
    reader.readAsText(event.srcElement.files[0]);
});
document.body.prepend(f);
f.focus();

Made another 2 bookmarklets, now using aria-label & className (hasOutput), etc.

javascript:f=document.forms,longest=0;frm=0;for(i=0;i<f.length;i++)if(f[i].length>longest){longest=f[i].length;frm=f[i]}values={};for(a=0;a<longest;a++){el=frm[a];switch(el.type){case "checkbox":values[el.name]=el.checked;break;case "radio":if(el.checked)values[el.name]=el.value;else if(values[el.name]===undefined){values[el.name]=false;continue}break;case "select-one":values[el.name]=el.selectedIndex<0?-1:el.options[el.selectedIndex].value;break;case "select-multiple":values[el.name]=[];for(i=0;i<el.options.length;i++){if(el.options[i].selected)values[el.name].push(el.options[i].value)}break;case "fieldset":case "button":case "submit":case "reset":case "file":case "hidden":case undefined:continue;default:values[el.name]=el.value}var old=values[el.name];delete values[el.name];if(!old)continue;var names=[],passed={};do{var el2=el;do{if(el2.className=="hasOutput"&&!passed[el2.innerText]){names.push(el2.innerText);passed[el2.innerText]=1}el2=el2.firstElementChild}while(el2)if(el.ariaLabel&&!passed[el.ariaLabel]){names.push(el.ariaLabel.trim());passed[el.ariaLabel]=1}}while(el=el.parentElement)if(names[0]=="Měna"&&old=="aed")continue;names.reverse();el=values;for(b=0;b<names.length-1;b++){if(!el[names[b]])el[names[b]]={};el=el[names[b]]}el[names[names.length-1]]=old}dataStr="data:application/json;charset=utf-8,"+encodeURIComponent(JSON.stringify(values));dwn=document.createElement("A");dwn.setAttribute("href",dataStr);dwn.setAttribute("download",location.href.split("/").pop()+".json");dwn.click();dwn.remove();undefined

f = document.forms, longest = 0; frm = 0;
for (i = 0; i < f.length; i++)
    if (f[i].length > longest) { longest = f[i].length; frm = f[i] }
values = {};
for (a = 0; a < longest; a++) {
    el = frm[a];
    switch (el.type) {
        case "checkbox": values[el.name] = el.checked;
            break;
        case "radio":
            if (el.checked)
                values[el.name] = el.value;
            else if (values[el.name] === undefined)
            {
                values[el.name] = false;
                continue;
            }
            break;
        case "select-one": values[el.name] = el.selectedIndex < 0 ? -1 : el.options[el.selectedIndex].value; break;
        case "select-multiple": values[el.name] = [];
            for (i = 0; i < el.options.length; i++) {
                if (el.options[i].selected)
                    values[el.name].push(el.options[i].value)
            }
            break;
        case "fieldset":
        case "button":
        case "submit":
        case "reset":
        case "file":
        case "hidden":
        case undefined:
            continue;
            break;
        default: values[el.name] = el.value
    }
    var old = values[el.name];
    delete values[el.name];
    if (!old) continue;
    var names = [], passed = {};
    do {
        var el2 = el;
        do {
            if (el2.className == "hasOutput" && !passed[el2.innerText])
            {
                names.push(el2.innerText);
                passed[el2.innerText] = 1;
            }
            el2 = el2.firstElementChild;
        } while (el2)
        if (el.ariaLabel && !passed[el.ariaLabel])
        {
            names.push(el.ariaLabel.trim());
            passed[el.ariaLabel] = 1;
        }
    } while (el = el.parentElement)
    if (names[0] == "Měna" && old == "aed") continue;
    names.reverse();
    el = values;
    for(b = 0; b < names.length - 1; b++)
    {
        if (!el[names[b]])
            el[names[b]] = {}
        el = el[names[b]];
    }
    el[names[names.length-1]] = old;
}
dataStr = "data:application/json;charset=utf-8," + encodeURIComponent(JSON.stringify(values));
dwn = document.createElement("A");
dwn.setAttribute("href", dataStr);
dwn.setAttribute("download", location.href.split("/").pop() + ".json");
dwn.click();
dwn.remove(); undefined

And back by

javascript:function setFormData(valuesBack){f=document.forms,longest=0;frm=0;for(i=0;i<f.length;i++)if(f[i].length>longest){longest=f[i].length;frm=f[i]}values={};for(a=0;a<longest;a++){el=frm[a];switch(el.type){case "checkbox":values[el.name]=el;break;case "radio":if(el.checked)values[el.name]=el;else if(values[el.name]===undefined){values[el.name]=el;continue}break;case "select-one":values[el.name]=el;break;case "select-multiple":values[el.name]=el;break;case "fieldset":case "button":case "submit":case "reset":case "file":case "hidden":case undefined:continue;break;default:values[el.name]=el}var names=[],passed={};var oldEl=values[el.name];do{var el2=el;do{if(el2.className=="hasOutput"&&!passed[el2.innerText]){names.push(el2.innerText);passed[el2.innerText]=1}el2=el2.firstElementChild}while(el2)if(el.ariaLabel&&!passed[el.ariaLabel]){names.push(el.ariaLabel.trim());passed[el.ariaLabel]=1}}while(el=el.parentElement)names.reverse();el=valuesBack;for(b=0;b<names.length&&el;b++){el=el[names[b]]}if(el===undefined)continue;val=el;el=oldEl;if(el.constructor==HTMLCollection){for(p=0;p<el.length;p++){el[p].checked=(el[p].value===val)}}else switch(el.type){case "checkbox":el.checked=val;break;case "select-one":if(val<0)el.selectedIndex=val;else{for(o=0;o<el.options.length;o++){if(el.options[o].value==val)el.selectedIndex=o}}break;case "select-multiple":for(o=0;o<el.options.length;o++){el.options[o].selected=(val.indexOf(el.options[o].value)>-1)}break;default:el.value=val}}}f=document.createElement("input");f.type="file";f.onchange=(function(){reader=new FileReader();reader.onload=(function(theFile){return function(e){json=JSON.parse(e.target.result);setFormData(json);return}})(event.srcElement.files[0]);reader.readAsText(event.srcElement.files[0])});document.body.prepend(f);f.focus();

function setFormData(valuesBack) {
    f = document.forms, longest = 0; frm = 0;
    for (i = 0; i < f.length; i++)
        if (f[i].length > longest) { longest = f[i].length; frm = f[i] }
    values = {};
    for (a = 0; a < longest; a++) {
        el = frm[a];
        switch (el.type) {
            case "checkbox": values[el.name] = el;
                break;
            case "radio":
                if (el.checked)
                    values[el.name] = el;
                else if (values[el.name] === undefined)
                {
                    values[el.name] = el;
                    continue;
                }
                break;
            case "select-one": values[el.name] = el; break;
            case "select-multiple": values[el.name] = el;
                break;
            case "fieldset":
            case "button":
            case "submit":
            case "reset":
            case "file":
            case "hidden":
            case undefined:
                continue;
                break;
            default: values[el.name] = el
        }
        var names = [], passed = {};
        var oldEl = values[el.name];
        do {
            var el2 = el;
            do {
                if (el2.className == "hasOutput" && !passed[el2.innerText])
                {
                    names.push(el2.innerText);
                    passed[el2.innerText] = 1;
                }
                el2 = el2.firstElementChild;
            } while (el2)
            if (el.ariaLabel && !passed[el.ariaLabel])
            {
                names.push(el.ariaLabel.trim());
                passed[el.ariaLabel] = 1;
            }
        } while (el = el.parentElement)
        names.reverse();
        el = valuesBack;
        for(b = 0; b < names.length && el; b++)
        {
            el = el[names[b]];
        }
        if (el === undefined) continue;
        val = el; el = oldEl;

        if(val === true) val = false;
        else val = val.split('').reverse().join('');

        if (el.constructor == HTMLCollection) {
            for (p = 0; p < el.length; p++) {
                el[p].checked = (el[p].value === val)
            }
        } else switch (el.type) {
            case "checkbox": el.checked = val;
                break;
            case "select-one":
                if (val < 0) el.selectedIndex = val;
                else {
                    for (o = 0; o < el.options.length; o++) {
                        if (el.options[o].value == val) el.selectedIndex = o
                    }
                }
                break;
            case "select-multiple":
                for (o = 0; o < el.options.length; o++) {
                    el.options[o].selected = (val.indexOf(el.options[o].value) > -1)
                }
                break;
            default: el.value = val
        }
        }
}
f = document.createElement("input");
f.type = "file";
f.onchange = (function () {
    reader = new FileReader();
    reader.onload = (function (theFile) {
        return function (e) {
            json = JSON.parse(e.target.result);
            setFormData(json);
            return;
        }
    })(event.srcElement.files[0]);
    reader.readAsText(event.srcElement.files[0]);
});
document.body.prepend(f);
f.focus();
Jan
  • 2,178
  • 3
  • 14
  • 26
  • Btw there are missing SELECT's OPTIONs with text, but without (invisible) value. Suppose it is rare anyway and would need bit more code - maybe it would be easier to fill val by text if they are missing during get (and/or set). – Jan Apr 21 '20 at 06:48
  • Sorry, I work in healthcare and I've been somewhat swamped with patients. I've even extended my work week into the weekends. I also teach at a university and we are hitting the end of the semester which is busy too! However, this looks amazing and was way more than I had hoped for! Thank you so much! This seems to work well under Chrome (I haven't tested FF either, but Chrome works with it). – Robert Apr 28 '20 at 17:29
  • 1
    You know - when you test something, you improve it, ... Love to have something reusable in answers and in case it can fit to bookmarklet ;-) You can use it anwhere from favorites or put it to Greasemonkey in case it would be too big... – Jan Apr 28 '20 at 22:40
1

There are multiple solutions to do what you're asking, the issue is data persistence. I would encourage a solution that saves the form submissions, and in-progress form submissions, to be saved to a DB or the cloud. We can talk about those if you're interested.

Generally, web browsers weren't made to create, save, and load data simply on the client. A server would be involved with the process. However, there is the option to use local storage on any modern browser. It's much more robust than using cookies. To get you started, take a look, at this example:

https://www.telerik.com/blogs/save-for-later-feature-in-forms-using-localstorage

It would also need an ID (like a date/time stamp) for each form submission, and a dialog box to pick which one to load, or create a new form submission.

Tom C
  • 33
  • 1
  • 8