HTML フォームのデータを自動で保存する。(ローカルストレージを使用)

フォームの要素の値が変化するたびに呼び出され、ローカルストレージに保存する
webアプリの環境設定に利用するために書いてみた。


いつも少し書いては、試しながらコーディングする。
今回は、すべて頭の中で動かしながら書いてみた。
単純なミスが1つ、構造的な順序のミスが2つ、意外に少なかった。
ie は、form要素の onchange を拾えないらしいので、動かない
JSON.(stringify || parse) を利用しているので使えないものは、動かない。


追記
callBackFunction が動作するように追加した
disabled も追加。

/* HTMLのFORM要素のエレメントの変数の値をローカルストレージに自動的に保存。
formにonchangeイベントを仕掛け、変更があった場合 form.elements の名前を基準に
オブジェクトにします。nameが複数ある場合は、配列となります。

「使い方」
// 初期設定
var obj = AutoFormController.create (form[, cbfunc[, disabled]]]); 

// 一時無効
obj.disabled = true;

// 一時無効状態でも強制的に保存する
obj.save (true);

// form要素のエレメントの値を取得する
var value = obj.getValues ();

// form要素のエレメントに値を設定する
obj.setValues ({hoge: [1,2,3], fuga: 'abc'});
//以下のようになる
<input type="text" name="hoge" value="1"> ....1
<input type="radio" name="hoge" value="2">....checked
<select name="hoge" multiple>
  <option value="1">1....selected
  <option value="2">2....selected
  <option value="3">3....selected
</select> 
<input type="text" name="fuga" value=""> .... 'abc'
<input type="checkbox" name="fuga" value="abc">.... checked

*/
(function () {

  function Afc (form, func, disabled) {
    this.form = form;
    this.func = func;
    this.disabled = disabled;
  }
  
  
  function setValue2 (es, vs) {
    var i, e, j, opts, o;
    var vlen;

    es = [].concat (es);//配列化
    vs = [].concat (vs);
    vlen = vs.length - 1;
    
    for (i = 0; e = es[i]; i+= 1) {
      switch (e.type) {
      case 'radio' : case 'checkbox' :
        e.checked = (-1 < vs.indexOf (e.value));
        break;
      case 'select-one' : case 'select-multiple' :
        opts = e.options;
        for (j = 0; o = opts[j++]; )
          o.selected = (-1 < vs.indexOf (o.value));
        break;
      default :
        e.value = vs[(vlen < i) ? vlen: i];
        break;
      }
    }
  }
  
  function setValue (values) {
    var es = this.form.elements;
    var i, e, v;
    
    for (i = 0; e = es[i++]; )
      if ('undefined' !== typeof (v = values[e.name]))
        setValue2 (e, v);
  }

  
  function vPush (e) {
    var vs = this;
    var nm = e.name;
    
    if (! nm)
      throw new Error ('Element has not a name.');
    
    if ('undefined' === typeof vs[nm])
      vs[nm] = e.value || '';
    else
      vs[nm] = [].concat (vs[nm], e.value || '');
  }
  
  //name が2つ以上ある場合は、値を配列にする
  function getValue () {
    var es = this.form.elements;
    var vs = { };
    var e, i, j, opts, o;
    
    for (i = 0; e = es[i++]; ) {
      switch (e.tagName) {
      case 'INPUT' : case 'TEXTAREA' : case 'SELECT' :
        switch (e.type) {
        case 'button' : case 'submit' : case 'reset' :
          break;
    
        case 'radio' : case 'checkbox' :
          if (e.checked)
            vPush.call (vs, e);
          break;
    
        case 'select-one' :
          vPush.call (vs, e);
          break;
    
        case 'select-multiple' :
          opts = e.options;
          for (j = 0; o = opts[j++]; ) {
            if (o.selected)
              vPush.call (vs, { name: e.name, value: o.value });
          }
          break;
    
        default :
          vPush.call (vs, e);
        }
      }
    }
    return vs;
  }
  
  //_____________________________________________
  
  function save (coerce) {
    if (! coerce)
      if (this.disabled)
        return;

    localStorage.setItem (
      this.form.id || this.form.name,
      JSON.stringify (getValue.call (this))
    );
  }
  
  
  function load (coerce) {
    if (! coerce)
      if (this.disabled)
        return;

    var d = localStorage.getItem (this.form.id || this.form.name);
    if (d) setValue.call (this, JSON.parse (d));
  }
  
  
  function create (form, func, disabled) {
    if (arguments.length < 1 || 'FORM' !== form.tagName)
      throw new Error ('Not form HTML elements');
    
    var id = form.id || form.name || null;
    var obj;
    
    if (null === id)
      throw new Error ('Form does not have a name.');

    obj = new Afc (form, func || null, disabled || false);
    
    obj.load ();

    function cbFunc (event) {
      save.call (obj);
      if ('function' === typeof this.func)
        this.func.call (this, event);
    }
        
    form.addEventListener ('change', cbFunc, false);
    form = null;
    
    return obj;
  }

  //__________________________________
  
  Afc.prototype.save = save;
  Afc.prototype.load = load;
  Afc.prototype.getValues = getValue;
  Afc.prototype.setValues = setValue;

  Afc.create = create;
  
  this.AutoFormController = Afc;
}) ();