JSONを利用して(テーブル式)フォームを形成する
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title></title>
<meta http-equiv="content-language" content="ja">
<meta http-equiv="Cache-Control" content="no-cache">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="format-detection" content="telephone=no">
<link rel="stylesheet" href="form_table.css" type="text/css">
<style>
</style>
</head>
<body>
<form method="post" enctype="multipart/form-data" id="FF">
<table id="T"></table>
<button onclick="console.log($F.getValue(),FF); return false;">フォームの値を取得</button>
<input type="submit" value="submit">
</form>
<script>
const JSON = [
{title:['世帯名',{rowspan:1}], name:'SETAI'},
{title:'住所', name:'POSTCODE', attr:{placeholder:'〒'}},
{name:'ADD' },
{name:'ADD' },
{title:'電話', name:'TEL'},
{name:'TEL' },
{name:'TEL' },
{title: '性別', name:'SEIBETU', type: 'radio', option: [
['男性','man', 0,1],
['女性','woman'],
['不詳','',1,0],
]},
{title: '言語', name:'GENGO', type: 'checkbox', option: [
['PHP','PHP'],
['JavaScript','JS',1],
['BASIC','BASIC'],
]},
{title: 'IQ', name:'IQ', type: 'select', option: [
['--選択して下さい--','',0],
['頭が良い','120'],
['普通','100'],
['無理','80'],
]},
{title: '地区', name:'TIKU', type: 'datalist', option: [
'種市','小子内','有家','中野','大野',
]},
{title: 'Memo', name:'MEMO', type: 'textarea', attr:{rows:10}},
// {title: '', name:'', value: '', type: null, option: [], attr: {}},
];
class $F {
/*
独自のJSON形式で模したフォーム要素を具現化してテーブル形式にフォームを構成する
*/
static mkTable (aryArg = [], tbody = document.createElement ('tbody')) {
const E = (n,a={},c=[],e)=> (e=document.createElement(n),Object.entries(a).forEach(([a,b])=>b !== false && e.setAttribute(a,b)),e.append(...c),e);
let tr, th, attr, cnt = 0;
for (let obj of aryArg) {
let t = obj.title;
tr = tbody.insertRow ();
if (null != t) {
if (1 < cnt)
th.rowSpan = cnt;
tr.append (th = document.createElement ('th'));
if (Array.isArray (t)) {
[t, attr] = t;
Object.entries (attr).forEach (_=> th.setAttribute (..._));
}
th.textContent = t;
cnt = 1;
} else
cnt++;
this.mkInput (obj, tr.insertCell ());
}
return tbody;
}
/*
フォーム要素を生成
*/
static mkInput (mold, parent = new DocumentFragment ()) {
// Object.assign では list属性を変更できないので setAttribute を利用
const E = (n,a={},c=[],e)=> (e=document.createElement(n),Object.entries(a).forEach(([a,b])=>b !== false && e.setAttribute(a,b)),e.append(...c),e);
let {name, value = '', attr = {}, option = [], type = 'text'} = mold;
let e = [], id;
type = mold.type = type.toLowerCase ();
switch (type) {
case 'textarea' :
e.push (E ('textarea', Object.assign({name, value},attr))); break;
case 'select' :
attr.type ='select-one';
case 'select-multiple' : case 'select-one' :
e.push (E('select', Object.assign({name, value}, attr), option.map (_=> new Option (..._))));
break;
case 'checkbox' : case 'radio' :
e.push (...(option.map (([a,b,c,d=c])=>
E('label', attr, [E('input', {name, type, value:b, checked:!!c, defaultChecked:!!d}),a])))
);
break;
case 'datalist':
id = 'dl-' + name;
e.push (
E('input', Object.assign ({name, value, list:id}, attr)), //type は attr で上書き?
E('datalist', {id}, option.map(_=> new Option(_)))
);
break;
case 'image' :
e.push (E('input', Object.assign ({name, type, value, src:value}, attr)));
break;
default :
e.push (E('input', Object.assign ({name, type, value}, attr)));
break;
}
parent.append (...e);
return parent;
}
/*
フォーム要素群の値をオブジェクト型にして返す
同じname属性が複数 or select-multipleの場合 or checkbox は配列で返す
*/
static getValue (root = document.body, query = ':is(select,textarea,input)[name]:not([type="button"],[type="reset"],[type="submit"])') {
//フォームの要素の値を返す
const V = e=> {
switch (e.type) {
case'select-multiple': return [...e.selectedOptions].map(o=>o.value);
case'checkbox': if(! e.checked) return []; break;
case'radio': if (! e.checked) return; break;
}
return e.value;
};
//__
let obj = { };
for (let e of root.querySelectorAll (query)) {
let val = V (e);
let name = e.name;
if (obj.hasOwnProperty (name)) {
if (null == val) continue;
let x = obj[name];
if (null != x)
val = [].concat (x, val);
}
obj[name] = val;
}
return obj;
}
/*
*/
//オブジェクト型の値をフォーム要素群に転置
static setValue (obj , root = document.body, query = ':is(select,textarea,input)[name]:not([type="button"],[type="reset"],[type="submit"])') {
//要素に値を設置
const V = (e, val)=> {
let idx;
if (Array.isArray (val)) {//配列の場合は、転置ごとにその値を削除する
switch (e.type) {
case 'select-multiple' :
for (let op of e.options) {
idx = val.indexOf (e.value);
if (-1 < idx) {
val.splice (idx, 1);
e.selected = true;
} else
e.selected = false;
}
break;
case 'radio' : case'checkbox' :
idx = val.indexOf (e.value);
if (-1 < idx) {
val.splice (idx, 1);
e.checked = true;
} else
e.checked = false;
break;
default :
e.value = val.shift () || '';
break;
}
}
else {//転置用の値が単一ならば、複数の同じname属性の要素に同じ値を転置する
switch (e.type) {
case 'select-multiple' :
e.options.forEach (o=> o.selected = o.value == val);
break;
case 'radio' : case'checkbox' :
e.checked = e.value == val;
break;
default :
e.value = val || '';
break;
}
}
};
//__
let
val = Object.assign ({ }, obj),//copy
es = root.querySelectorAll (query);
this.reset ();
for (let e of es)
V (e, val[e.name]);
}
static reset (root = document.body, query = ':is(select,textarea,input)[name]:not([type="button"],[type="reset"],[type="submit"])') {
let es = root.querySelectorAll (query);
for (let e of es) {
switch (e.type) {
case 'select-one' : case 'select-multiple' :
for (let op of e.options)
op.selected = op.defaultSelected;
break;
case 'radio' : case 'checkbox' :
e.checked = e.defaultChecked;
break;
default :
e.value = e.defaultValue || '';
}
}
}
}
$F.mkTable (JSON,T);
$F.setValue ({MEMO: 'abc', TIKU:'中野', GENGO:['PHP','BASIC'], SEIBETU:'woman',ADD:[,2,]});
//alert(8);
$F.reset ();
</script>
ちょっと変更