HTML
<!DOCTYPE hrml>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>SELECTをPHPとで連動する</title>
<body>
<header>
<nav>
<ul>
<li><select id="S0"></select>
<li><select id="S1"></select>
<li><select id="S2"></select>
</ul>
</nav>
</header>
<script>
{
const
PHP_FUNCTION_NAME = 'ChainSelector',
dispatchChangeEvent = element => {
let
doc = element.ownerDocument,
event = doc.createEvent ('HTMLEvents');
event.initEvent ('change', true, true);
element.dispatchEvent (event);
},
replaceOptions = (mapObj, obj) => {
Object.keys (obj).forEach (name => {
let
e = mapObj[name],
memory = null,
current = null;
while (e.hasChildNodes ())
e.removeChild (e.firstChild);
for (let r of obj[name]) {
let
{ group = null, text = null, value = null, defaultSelected = false, selected = false } = r,
option = new Option (('undefined' === typeof text ? value: text), value, defaultSelected, selected);
if (memory !== group) {
let optgroup = document.createElement ('optgroup');
optgroup.setAttribute ('label', group);
current = group ? e.appendChild (optgroup): null;
}
(current || e).appendChild (option);
memory = group;
}
})
},
fileLoader = function (file, arg = null) {
let req = new XMLHttpRequest ();
req.open ('POST', file, false);
req.setRequestHeader ('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
req.setRequestHeader ('Pragma', 'no-cache');
req.setRequestHeader ('Cache-Control', 'no-cache');
req.setRequestHeader ('X-Requested-With', 'XMLHttpRequest');
req.send (arg !== null ? JSON.stringify (arg): null);
return req.responseText;
};
class ChainSelector {
constructor (selects = [ ], link, cbFunc = null) {
if (2 > arguments.length)
throw new Error ('引数が不足です');
let
map = new Map,
map2 = { },
es = [...selects];
es.forEach (e => {
let key = e.id || e.name;
if (key) {
map.set (e, key);
map2[key] = e;
e.addEventListener ('change', this, false);
}
});
this.map = map;
this.map2 = map2;
this.link = link;
this.cbFunc = cbFunc;
this.reLoad ();
}
reLoad (...args) {
let
parameter = { func: PHP_FUNCTION_NAME, args },
res = fileLoader (this.link, parameter);
if (res)
replaceOptions (this.map2, JSON.parse (res));
}
default () {
let first = this.map.keys ().next ().value;
dispatchChangeEvent (first);
}
handleEvent (event) {
let { target } = event;
if (this.map.has (target)) {
let
name = this.map.get (target),
value = target.value;
this.reLoad (name, value);
if ('function' === typeof this.cbFunc)
this.cbFunc.call (this, event);
}
}
}
this.ChainSelector = ChainSelector;
}
let
selects = document.querySelectorAll ('nav select'),
callside = 'call.php',
cbfunc = (event) => location.hash = event.target.value,
hashchangeHandler = (event) => console.log (location.hash);
window.addEventListener ('hashchange', hashchangeHandler, false);
new ChainSelector (selects, callside, );
</script>
<?php
const CHAR_CODE = 'UTF-8';
mb_internal_encoding (CHAR_CODE);
mb_regex_encoding (CHAR_CODE);
header ('Content-type: application/json; charset='. CHAR_CODE);
header ('X-Content-Type-Options: nosniff');
echo json_encode (
call_user_func_array (
'call_user_func_array',
callBackFuncAplly (
json_decode (file_get_contents ('php://input'), true),
array ('func', 'args')
)
)
);
exit;
function ChainSelector ($name = '', $selected_value, $chain = array ()) {
$ary = array ();
switch ($name) {
case 'S0' :
switch ($selected_value) {
case 'A':
$ary = array (
array ('value' => 'AA', 'text' => 'aa'),
array ('value' => 'AB', 'text' => 'ab')
);
break;
case 'B':
$ary = array (
array ('value' => 'BA', 'text' => 'ba'),
array ('value' => 'BB', 'text' => 'bb')
);
break;
}
$mess = array (
'value' => '',
'text' => count ($ary) ? '選択してください': 'データはありません',
'selected' => true
);
$chain['S1'] = array_merge (array ($mess), $ary);
return ChainSelector ('S1', $selected_value, $chain);
break;
case 'S1' :
switch ($selected_value) {
case 'AA':
$ary = array (
array ('value' => 'AAa', 'text' => 'aaa'),
array ('value' => 'AAb', 'text' => 'aab')
);
break;
case 'AB':
$ary = array (
array ('value' => 'ABA', 'text' => 'aba'),
array ('value' => 'ABB', 'text' => 'abb')
);
break;
case 'BA':
$ary = array (
array ('value' => 'BAA', 'text' => 'baa'),
array ('value' => 'BAB', 'text' => 'bab')
);
break;
case 'BB':
$ary = array (
array ('value' => 'BBA', 'text' => 'bba'),
array ('value' => 'BBB', 'text' => 'bbb')
);
break;
}
$mess = array (
'value' => '',
'text' => count ($ary) ? '選択してください': 'データはありません',
'selected' => true
);
$chain['S2'] = array_merge (array ($mess), $ary);
break;
case 'S2':
break;
default :
$ary = array (
array ('value' => 'A', 'text' => 'a'),
array ('value' => 'B', 'text' => 'b'.$name)
);
foreach ($ary as &$a)
$a['selected'] = ($a['value'] === $selected_value);
$mess = array (
'value' => '',
'text' => count ($ary) ? '選択してください': 'データはありません',
'selected' => true
);
$chain['S0'] = array_merge (array ($mess), $ary);
return ChainSelector ('S0', $selected_value, $chain);
break;
}
return $chain;
}
function callBackFuncAplly ($data, $args) {
$cnt = count ($args);
$rst = array ();
for ($i = 0; $i < $cnt; $i += 1)
if (isset ($data[$args[$i]]))
$rst[] = $data[$args[$i]];
else
throw new Exception ('変数がありません', 9);
return $rst;
}