formの要素を制御する。 (過去に書いたものを焼き直し)
(function () { function FormController (form) { this.form = form; } var hashTypeCheck = {// bit0: 単数or複数, bit1:input, bit2: select TEXTAREA: { 'textarea': 1 }, SELECT : { 'select-one' : 5, 'select-multiple': 4 }, INPUT : { tel : 1, url : 1, email : 1, hidden: 1, text : 1, search : 1, password : 1, date : 1, month : 1, week : 1, time : 1, datetime : 1, 'datetime-local' : 1, number : 1, range: 1, color : 1, radio: 3, checkbox : 2 } }; // select 要素の option を更新する function replaceSelect (select, textList, valueList, selectNo, defaultNo) { valueList = (valueList) ? valueList: textList; var doc = select.ownerDocument; var option = doc.createElement ('option'); var i, I = textList.length; var clone; while (i = select.firstChild) { select.removeChild (i); } if (selectNo instanceof Array) ; if ('number' === typeof selectNo) selectNo = [selectNo]; else selectNo = []; for (i = 0; i < I; i++) { clone = option.cloneNode (false); // new Option clone.value = valueList[i]; clone.text = textList[i]; clone.selected = (0 <= selectNo.indexOf (i)); // clone = new Option (textList[i],valueList[i]); select.appendChild (clone); //add ? } select.options[selectNo].selected = true; } // フォームのコントロールか? function isControls (e) { var hash = hashTypeCheck[e.tagName]; var no = hash ? ('type' in e) ? (hash[e.type] || 0): 1: 0; return no; } // ノードリストか? function isNodeList (list) { return (list.nodeType !== 1); } // 配列にする function toArray (item) { return isNodeList (item) ? Array.prototype.slice.call (item, 0): [item]; } //使われている name をすべて取得 function getAllControllsName (form) { return toArray (form.elements) .filter (isControls) .reduce (normalize, []); } // 単一化 function normalize (a, b) { return (-1 < a.indexOf (b.name)) ? a: a.concat (b.name); } // radio, checkbox の value function getCheckboxValue (e) { return e.hasAttribute ('value') ? e.value: e.checked ? 'on': 'off'; } // select option の value function getOptionValue (e) { return e.hasAttribute ('value') ? e.value: e.text; } // option を捜査し、選ばれている値を収集する function getSelectedValue (a, opt) { return (opt.selected) ? a.concat (getOptionValue (opt)): a; } // コントロールから値の取得 function getValue (a, e, i) { switch (isControls(e)) { case 1 : case 5 : a.push (e.value); break; //5:select-one case 2 : case 3 : if (e.checked) a.push (getCheckboxValue (e)); break; case 4 : a.concat (toArray (e.options).reduce (getSelectedValue, [])); break; //selct-multiple } return a; } // 名前から値の取得 function getControllValue (ctrls) { var result = toArray (ctrls).reduce (getValue, [ ]); return ctrls.length === 1 ? result[0]: result; } /* 対象の要素を集めて、最後に .value 値を集めようと思ったが、option に無い 場合は、.text から、checkbox には、'on' が必要だったりであきらめた。 */ //_______________________ // 名前からオブジェクトで収集する function emptyObject (a, b) { a[b.name] = undefined; return a; } // 引数の変数のタイプにより、フォーム要素から値を取得して代入する function parser_get () { // arguments が無いのなら、全ての名前をオブジェクトにして再帰で呼び出す if (0 === arguments.length) { return parser_get.call (this, toArray (this.form.elements).filter (isControls).reduce (emptyObject, { })); } var result = [ ]; var i, arg; for (i = 0; arg = arguments[i++]; ) { switch (getType (arg)) { case 'string' : case 'number' : result.push (getControllValue (this.form.elements[arg])); break; case 'array' : result.push (result.concat (arg.map (parser_get, this))); break; case 'object' : var obj = { }; for (var name in arg) if (arg.hasOwnProperty (name)) obj[name] = parser_get.call (this, name, arg[name]); result.push (obj); break; } } return arguments.length === 1 ? result[0]: result; } //_______________________ // 要素が radio もしくは checkbox か? function isRadioChecbox (e) { return (e.type === 'radio') || (e.type === 'checkbox'); } // 要素がセレクトか? function isSelect (e) { return ('SELECT' === e.tagName); } // 普通に e.value = xxxx で可能なもの function isOther (e) { var hash = hashTypeCheck[e.tagName]; return hash ? ('type' in e) ? (1 === hash[e.type]): false : false; } // テキスト、数値 用の値設置 function setValue (e, i) { var idx = this.length; //コントロールの数と配列の数が合わないときは、配列の最後の値を代入 e.value = this[(idx <= i) ? idx - 1: i] || ''; } // select-option 用の値設置 function setOptionSelected (e) { e.selected = (0 <= this.indexOf (getOptionValue (e))); } // select 用の値設置 function setValueSelect (e) { toArray (e.options).forEach (setOptionSelected, this); } // radio, checkbox 用の値設置 function setValueRdioCheckbox (e) { e.checked = (0 <= this.indexOf (getCheckboxValue (e))); } function setControllValue (elements, name, value) { switch (getType (value)) { case 'string' : case 'number' : value = [value]; break; case 'array': break; default : value = [String (value)]; } var es = elements[name]; if (es) { es = toArray (es); es.filter (isOther).forEach (setValue, value); es.filter (isSelect).forEach (setValueSelect, value); es.filter (isRadioChecbox).forEach (setValueRdioCheckbox, value); } } // フォームの要素に、値をセットする。 (再帰で呼び出す) function parser_set (/* param */) { var len = arguments.length; var i = 0; var name, parm, type; while (i < len) { parm = arguments[i++]; switch (getType (parm)) { case 'string' : case 'number' : setControllValue (this.form.elements, parm, arguments[i++]); break; case 'array' : parm.forEach (parser_set, this); break; case 'object' : for (var name in parm) { if (parm.hasOwnProperty (name)) { parser_set.call (this, name, String (parm[name]) || ''); } } break; } } } function getType (o) { var t = typeof o; if ('string' === t || o === null) return 'string'; if ('number' === t) return 'number'; if (o instanceof Array) return 'array'; if ('object' === t) return 'object'; } //_______________________ //フォームのコントロールにフォーカスが当たるとキャレットを最後尾に移動する function focusHandler (event) { if (! FormController.focusMode) return; var target = event.target; if ('INPUT' !== target.tagName) return; if ('text' === target.type) { var len = target.value.length; target.setSelectionRange (len, len); event.preventDefault (); } } function namedItem (name) { return this.form.elements.namedItem (name); } // フォームのリセット function reset () { this.form.reset (); return this; } // オブジェクト作成 function create (form) { if ((form) && ('FORM' === form.tagName)) return new FormController (form); return null; } //_______________________ document.addEventListener ('focus', focusHandler, true); FormController.prototype.setControllsValue = parser_set; FormController.prototype.getControllsValue = parser_get; FormController.prototype.reset = reset; FormController.prototype.name = namedItem; FormController.create = create; FormController.replaceSelect = replaceSelect; FormController.focusMode = true; // フォーカスが当たるとキャレットを最後尾にする this.FormController = FormController; /* * フォームのコントロールを操作する * var obj = FormController.create (document.querySelector ('form')); 値の取得 object.getControlsValue (parm0, parm1); 引数は、取得したいコントロールの名前とし、その属性にあった形で返す string : そのコントロールの名前の値を返す (その要素が複数であれば配列として返す) array : 取得したい名前の配列には、その値を配列にして返す object : 含まれるメゾットの名前を元に、値を返す none : form に含まれる全ての名前と値を、オブジェクトにして返す 値の設定 obj.setControllsValue (parm0, parm1, ...); 引数は、設定したいコントロールの名前と値の順とし、その属性にあった形で 指定できる string : そのコントロールの名前、次の引数を値として設定する [name0, [1,3,5,7]] select などで複数指定可の場合 名前の要素数が設定値の配列の数より多い場合、配列の最後の値が 以降の全てに適用される array : 名前、設定値の順で配列とする。 [name0, value0, name1, value1, ....] [[name0, value0], [name1, value1], [,]....] object : 含まれる名前を列挙し、その値を設定値とする コントロールにフォーカスが当たったら、キャレットを最後尾に移動する obj.focusMode = true; or false */ })();
ie は、相変わらず無視。多いにバグが含んでいる。
フォームのコントロールの値をオブジェクトで受け取ったり、設定したり。