Form cache?

Hi,

I need to create a website which has nothing but one big form (questionnaire) with many inputs. For the user it is a mess if they start to fill the form and at one point they recognize that they have not all Informationen yet and so they cannot continue for maybe a few days.
Of course no one will let the page open for days.

So my idea is, why not cache all inputs of the form in local storage so that if he closes the page and reopens it later, he will not loose his already filled data.

How to develop this is clear but I ask myself, if this is not such an often needed scenario that there is some ready to use solution anywhere in the web?

Did find this

Here is a very rough around the edges implementation from me. Only tested in chrome, it stores on the change event firing.

4 Likes

An itch that needed scratching, have refactored and commented the last script. Only tested on a very basic form.

// Takes a source DOM element and copies selected attributes and
// their values into a new object.
// @param(HTMLElement) elem
// @param(Array) attributes = Array of string attribute names
// @returns(Object)
const objectFromAttributes = function(elem, attributes = []) {
  const clone = {}

  for (const attribute of attributes) {
    const value = elem[attribute]
    // attributes with false and undefined values are ignored
    if (value ?? false) {
      clone[attribute] = value
    }
  }
  return clone
}

const deMethodize = (fn) => (arg0, ...args) => fn.apply(arg0, args)

// will work on array like objects e.g. HTMLCollections
const flatMap = deMethodize(Array.prototype.flatMap)

// check for empty objects
const notEmpty = (obj) => Object.keys(obj).length

window.addEventListener('DOMContentLoaded', () => {
  
  // Extracts user filled inputs and copies those entries to an Array
  // @param(HTMLElement) form
  // @param(Array) attributes = Array of attribute names e.g. ['value' , 'checked']
  // @param(Array) exclude = Array of types to exclude e.g. ['[type=password]', ...]
  // @returns(Array) An array of user inputs e.g. [[0, {value:'John'}], [3, {checked:true}]]
  const filterFormElements = function(form, { attributes = [], exclude = [] }) {
    if(!attributes.length) return []
    
    // create selector e.g. 'input:not([type=password],[type=hidden])'
    const selectedInputs = `input:not(${exclude.join(',')})`
    
    // using flatMap to filter selected inputs
    return flatMap(form, (elem, i) => {
      
      if (elem.matches(selectedInputs)) {
        const inputs = objectFromAttributes(elem, attributes)
        // ignore empty inputs
        if (notEmpty(inputs)) return [[i, inputs]]
      }

      return []
    })
  }
  
  const getFormData = function() {
    return JSON.parse(localStorage.getItem('form-storage')) ?? []
  }
  
  const deleteFormData = function() {
    localStorage.removeItem('form-storage')
  }
  
  const storeFormData = function(formData = []) {
    localStorage.setItem('form-storage', JSON.stringify(formData))
  }
  
  // Populate form with form data from localStorage
  // @param(HTMLElement) form
  // @param(Array) formData
  const populateForm = function(form, formData) {
    formData.forEach(([i, attributes]) => {
      const formElement = form[i]
      
      for (const key in attributes) {
        formElement[key] = attributes[key]
      }
    })
  }
  
  const form = document.querySelector('.save-form')

  form.addEventListener('change', (event) => {
    const form = event.currentTarget
    const formData = filterFormElements(
      form,
      {
        // input attributes to store
        attributes: ['value', 'checked'],
        // input types to ignore
        exclude: ['[type=password]', '[type=hidden]']
      }
    )
    
    storeFormData(formData)
  })

  // clear form-storage on submitting
  form.addEventListener('submit', deleteFormData)
  
  // clear form-storage on reset
  form.addEventListener('reset', deleteFormData)
  
  // populate form with stored data
  populateForm(form, getFormData())
})

Previous codepen updated.

edit: Updated to clear form storage on submittion.

3 Likes

Also to be courteous to the user, clean up the data on the page after the form is submitted successfully

2 Likes

Hi,

Thanks for the suggestions. I could not check them as I am super busy this week. Hopefully I can give it a try next week.