FORM要素の値をObuject型にしてJSON化しやすいようにする。もちろん jQuery なんて使用しない
function formToObject (form) { let es = form.elements, result = { }; for (let i = 0, e; e = es[i++]; ) { let type = e.type, name; switch (type) {//列挙したものはパス case 'submit' : case 'reset' : case 'button' : case 'image' : case 'fieldset': case 'file' : continue; break; //pass default : name = e.name; if (! name) continue; } let value = result[name], v; //一度でもnameが存在するものは null値で初期化 if ('undefined' === typeof value) result[name] = value = null; switch (type) { case 'select-multiple' : //v = Array.from (e.options).filter (o => o.selected).map (o => o.value) || []; //v = Array.from (e.options).reduce ((a, b) => (a.selected ? b.concat (a): b), []) v = []; if (-1 < e.selectedIndex) for (let i = 0, o, op = e.options; o = op[i++]; ) if (o.selected) v.push (o.value); break; case 'select-one' : if (0 > e.selectedIndex) continue; v = e.options[e.selectedIndex].value; break; case 'checkbox' : v = e.checked ? [e.value]: [ ]; break; case 'radio' : if (! e.checked) continue; //else "そのまま defaultを利用する" default : v = e.value; } result[name] = (Array.isArray (value)) ? value.concat (v) // 結合して配列に : (null !== value) ? [value, v] // すでに値があるのだから配列化 : v; } return result; }
今度は逆に、Object型のデータからFORM要素に値を設定する
function priority_elements (a, b) { let ref = { 'select-multiple': 10, 'checkbox': 9, 'select-one': 8, 'radio': 7 }; return (ref[a.type] || 0) < (ref[b.type] || 0); } function objectToForm (form, obj) { if (2 > arguments.length) throw new Error ('引数が足りません'); let elements = form.elements, result = { }; if (1 > elements.length) throw new Error ('FORMの要素がありません'); for (let name of Object.keys (obj)) {// !hasOwnProperty let target = elements[name]; if (! target) continue; if (target.nodeType === Node.ELEMENT_NODE) target = [target]; let values = obj[name]; if (! Array.isArray (values)) values = [String (values)]; let used = Array.of.apply ([ ], values);//value の配列で利用された値を保存 target = Array.from (target).sort (priority_elements); for (let e of target) { switch (e.type) { case 'submit' : case 'reset' : case 'button' : case 'image' : case 'fieldset': case 'file' : continue; break; //pass case 'select-one' : case 'select-multiple' : { let op = e.options; if (op.length) for (let o of e.options) { if (values.includes (o.value)) { o.selected = true; let idx = used.indexOf (o.value); if (-1 < idx) used.splice (idx, 1); } } } break; case 'radio' : case 'checkbox' : let checked = e.checked = values.includes (e.value); if (checked) { let idx = used.indexOf (e.value); if (-1 < idx) used.splice (idx, 1); } break; default: e.value = used.shift () || ''; break; } } } return result; }
以下メモ
function: formToObject
指定された form 要素の値を object型にして返します
name 属性が無いものは対象外とします
type 属性が 'submit','reset','button', 'image', 'fieldset', 'file' は対象外とします
type 属性が 'checkbox', 'select-multiple' は選択状態の要素がなくても空の配列を返します
name 属性が複数あると配列にして返します
type 属性が 'checkbox', 'radio' の選択状態が全くない場合は null を返します
function: objectToForm
指定された object 型の値を form の要素に設定します
name 属性が存在しない場合は無視されます
type 属性が 'submit','reset','button', 'image', 'fieldset', 'file' は無視されます
name 属性が同じで複数ある場合は type 属性を以下の順でソートしてから設定します
select-multiple -> checkbox -> select-one -> radio -> その他
type 属性が 'select-one', 'radio' の要素に、配列から複数の値を設定しようとした場合、
複数の選択項目の最後にマッチした value 値の要素を選択状態にします
type 属性が以下のもので選択条件にマッチする要素は、その全てが選択状態となります
また、それ以外の要素には利用されなかった配列の value 値から順に設定されます
"select-multiple", "checkbox", "select-one", "radio"