いまさら
ブロックスコープ内での関数の定義は、ブロックスコープの外からも呼び出せたのね。
FORM 要素の部品の要素の値を object型にして返す。その2
{ const TARGET_PARTS = ['INPUT', 'TEXTAREA', 'SELECT'],//対象となる form要素の部品 EXCLUDE_TYPE = ['submit', 'reset', 'button', 'image', 'fieldset', 'file'],//部品から除外する //引数の要素が formの部品なのか? isParts = function (e) { return (e) ? (TARGET_PARTS.includes (e.tagName)) ? ! EXCLUDE_TYPE.includes (e.type) : false : false; }, //引数がノードリストなのか? isNodeList = function (arg) { let rst = false; if (arg) if (! ('tagName' in arg)) if ('length' in arg) if ('item' in arg) rst = arg.item instanceof Function; return rst; }, //要素から使用可能な要素を取り出して配列化する pickupParts = function (es) { return (isNodeList (es) ? Array.from (es): [es]).filter (isParts); }, //引数の型を返す typeOf = function (arg) { return Object.prototype.toString.call (arg).slice (8, -1); }, //配列の中を文字列に変換する insideArrayToString = function (ary) { return ary.map (v => String (v)); }, //OPTION@SELECTの値を取得する getOptionValue = function (opt) { return opt.hasAttribute ('value') ? opt.value : opt.text; }, //同じ名前を持つ複数の部品がある場合並び替える sortByType = function (es) { let select_multiple = [ ], checkbox = [ ], select_one = [ ], radio = [ ], other = [ ]; es.forEach (e => { switch (e.type) { case 'select-multiple' : select_multiple.push (e); break; case 'checkbox' : checkbox.push (e); break; case 'select-one' : select_one.push (e); break; case 'radio' : radio.push (e); break; default : other.push (e); break; } }); return [ ].concat (select_multiple, checkbox, select_one, radio, other); }, /* checkbox をセットする */ setCheckbox = function (target = null, ...arg) { if (null === target) throw new Error ('要素がありません'); let rst = [ ];// checked できなかったリストを返す arg.forEach (a => { let values = [ ]; switch (typeOf (a)) { case 'Number' : case 'String' : values = [a]; break; case 'Array' : values = a; break; default : throw new Error ('無効な引数の型が指定されました'); break; } let idx = values.indexOf (target.value), flag = -1 < idx; if (target.checked = flag) values.splice (idx, 1); rst = rst.concat (values);//selected できなかった値をリスト化 }); return rst; }, //SELCT要素の optionを設定 setSelected = function (target = null, ...args) { if (null === target) throw new Error ('要素がありません'); let rst = [ ],// selected できなかったリストを返す opts = target.options; args.forEach (arg => { let values = [ ]; switch (typeOf (arg)) { case 'Number' : case 'String' : values = [arg]; break; case 'Array' : values = arg; break; default : throw new Error ('無効な引数の型が指定されました'); break; } for (let i = 0, opt; opt = opts[i]; i++) { if (values.length) { let val = opt.hasAttribute ('value') ? opt.value: opt.text, idx = values.indexOf (val), flag = -1 < idx; if ((opt.selected = flag)) values.splice (idx, 1); } } rst = rst.concat (values);//selected できなかった値をリスト化 }); return rst; }, //部品の値を返す value = function (es) { let singleFg = true, rst = [ ], i, e; es = pickupParts (es); if (! es.length) throw new Error ('指定された部品要素がありません'); for (i = 0; e = es[i]; i++) { let type = e.type; if (singleFg) { if ('select-multiple' === type || 'checkbox' === type) singleFg = false; } switch (type) { case 'select-multiple' : Array.from (e.options) .forEach (opt => opt.selected ? rst.push (getOptionValue (opt)): null); break; case 'select-one' : rst.push (getOptionValue (e.options[e.selectedIndex])); break; case 'checkbox' : case 'radio' : if (e.checked) rst.push (e.hasAttribute ('value') ? e.value: 'on'); break; default : rst.push (e.value); break; } } if (1 < rst.length) singleFg = false; return singleFg ? rst[0]: rst; }, // formの部品に値をセットする setVal = function (es, vals) { es = sortByType (pickupParts (es)); //部品はソート if (0 === es.length) throw new Error ('指定された部品要素がありません'); if (null === vals) vals = ''; switch (typeOf (vals)) { case 'Number' : case 'String' : vals = [vals]; break; case 'Array' : break; default : throw new Error ('無効な引数の型が指定されました'); break; } vals = insideArrayToString (vals); for (let i = 0, e; e = es[i]; i++) { switch (e.type) { case 'select-multiple' : case 'select-one' : vals = setSelected (e, vals); break; case 'checkbox' : case 'radio' : vals = setCheckbox (e, vals); break; default : e.value = value.length ? vals.shift (): ''; break; } } } //__________________________ class ExpForm { /* 引数を省略すると documentにある最初の form要素が対象となる */ constructor (form = document.forms[0]) { if ('FORM' !== form.tagName) throw new Error ('FORM要素ではありません'); this.form = form; } //__________________________ /*フォームの部品の値を取得する 引数の型により戻り値の型が変化する number => 部品番号の名前を元に値を返す string => 部品名を元に値を返す array => 配列の値で指定された番号もしくは部品名で値を取得し、それらの配列順で値を返す object => オブジェクトのキーを元に値を取得し、オブジェクトで返す */ getValue (arg = this.getEmptyObject ()) { let len = this.form.elements.length, e, es, name, rst; switch (typeOf (arg)) { case 'Number' : if (isNaN (arg)) throw new Error ('無効な数値が指定されました'); if (arg < 0) arg += len; e = this.form.elements[arg];//番号の要素の名前と同じ全要素を対象とする if (! e) throw new Error ('指定された部品要素がありません'); if (! (name = e.name)) throw new Error ('フォームの部品要素に名前がありません'); rst = value (this.form.elements[name]); break; case 'String' : rst = value (this.form.elements[arg]); break; case 'Array' : rst = arg.map (this.getValue, this); console.log ("rstary=",rst); break; case 'Object' : rst = Object.keys (arg).reduce ((a, b) => (a[b] = this.getValue (b), a), {}); break; default : throw new Error ('引数の型が無効です'); } return rst; } //__________________________ /* form の各部品に値をセットする 同名複数の部品がある場合 select-multiple, checkbox, その他の部品順に値を代入していく 代入された値は、配列から削除され余ったものは捨てられる 使い方 書式A setValue ('aaa', 123); //=>name属性が 'aaa'の最初の要素に 123を代入する B setValue ('aaa', [123, 456, 789]); //=>'aaa'の複数の要素に代入する C setValue ({aaa: 123, bbb:[456,789]}); オブジェクト型の構造のまま代入する */ setValue (arg, arg2) { if (! arguments.length) throw new Error ('引数の数が足りません'); let es = this.form.elements, name; switch (typeOf (arg)) { case 'Number' : if (isNaN (arg)) throw new Error ('無効な数値が指定されました'); if (arg < 0) arg += len; e = es[arg];//番号の要素の名前と同じ全要素を対象とする if (! e) throw new Error ('指定された部品要素がありません'); if (! (name = e.name)) throw new Error ('フォームの部品要素に名前がありません'); rst = setVal (es[name], arg2); break; case 'String' : setVal (es[arg], arg2); break; case 'Array' : rst = arg.map ((n, i) => this.setValue (n, arg2[i])); break; case 'Object' : Object.keys (arg).map ((key, i) => setVal (es[key], arg[key])); break; default : throw new Error ('引数の型が無効です'); } } //__________________________ //form要素の部品の(重複しない)nameリストを返す getNames () { return [... new Set (Array.from (this.form.elements).filter (isParts).map (e => e.name))]; } //__________________________ //nameをキーとする空のオブジェクトを作る getEmptyObject (obj = { }, defVal = null) { return this.getNames ().reduce ((a, b) => (a[b] = defVal, a), {}); } reset () { this.form.reset (); } //__________________________ //SELECT, DATALIST, OPTGROUP の子要素の option を配列を元に書き換える static replaceOptions (target, aryValue, aryText, defaultValue, selectedValue) { //引数確認 if (arguments.length < 2) throw new Error ('引数が足りません'); if (! /^(SELECT|DATALIST|OPTGROUP)$/.test (target.tagName)) throw new Error ('利用できる要素ではでありません'); if (! Array.isArray (aryValue)) throw new Error; if ('undefined' === typeof aryText) aryText = aryValue; if (aryValue.length !== aryText.length) throw new Error ('値とテキストの配列の数が違います'); let doc = target.ownerDocument, fgm = doc.createDocumentFragment (), opt = doc.createElement ('option'), sdv, dfv; switch (typeOf (defaultValue)) { case 'Number' : case 'String' : dfv = [defaultValue]; break; case 'Array' : dfv = defaultValue; break; case 'Undefined' : dfv = false; break; default : throw new Error ('無効な引数の型が指定されました'); break; } switch (typeOf (selectedValue)) { case 'Number' : case 'String' : sdv = [selectedValue]; break; case 'Array' : sdv = selectedValue; break; case 'Undefined' : sdv = false; break; default : throw new Error ('無効な引数の型が指定されました'); break; } //使用する配列全てを文字列化 aryValue = insideArrayToString (aryValue); aryText = insideArrayToString (aryText); if (dfv) dfv = insideArrayToString (dfv); if (sdv) sdv = insideArrayToString (sdv); //子要素の削除 while (e = target.firstChild) target.removeChild (e); //オプションの作成 aryValue.forEach ((v, i) => { let o = opt.cloneNode (false); o.text = aryText[i]; o.value = v; if (dfv) o.defaultSelected = dfv.include (v); if (sdv) o.selected = sdv.include (v); fgm.appendChild (o); }) target.appendChild (fgm); } } //__________________________ this.ExpForm = ExpForm; // this.isNodeList = isNodeList; }