form内の複数のselect要素に対して、一方を選択すると、他方のselectのoptionが自動的に絞り込まれるという処理を、以下のサンプルHTMLに対して、JavaScriptを使って実現してみようと思います。
サンプルコード
<form>
<select id="upper">
<option value="1">first</option>
<option value="2">second</option>
</select>
<select id="downer">
<option value="1_1">first_first</option>
<option value="1_2">first_second</option>
<option value="2_1">second_first</option>
<option value="2_2">second_second</option>
</select>
</form>
仕様は、
- id="upper" のselect要素のvalueと、id="downer"のselet要素の子のoption要素のvalueの1文字目を比較し、値が一致するdownerのoptionだけを、ユーザーが選択可能とする。
とします。
最初に作ったプログラム
最初に、条件に一致しないdownerのoption要素に、disabled属性を設定してみました。
サンプルコード
window.onload = function(){
var upper_select = document.getElementById("upper"),
downer_select = document.getElementById("downer");
autoSelectOptions(upper_select, downer_select);
upper_select.onchange = function(){autoSelectOptions(upper_select, downer_select)};
}
function autoSelectOptions(upper_select, downer_select) {
var selected_value = upper_select.value,
downer_ch = downer_select.childNodes,
first_hit_flag = true;
for (var i=0, len=downer_ch.length; i < len; i++) {
if (downer_ch[i].tagName && downer_ch[i].tagName === 'OPTION' ){
downer_ch[i].disabled = true; //一旦すべてのoptionを選択不可にする
downer_ch[i].selected = false; //選択状態をリセットする
if (selected_value === downer_ch[i].value.split('_')[0]) {
downer_ch[i].disabled = false; //選択可能にする
if (first_hit_flag === true) {
//一番上の選択可能optionを選択状態表示させる
downer_ch[i].selected = true;
first_hit_flag = false;
}
}
}
}
}
落とし穴にはまる
ふふふ、このくらい簡単々と調子に乗ってたら、このコードではIE6, 7では正しく動作しませんでした。IE6, 7 では、option要素にはdisabled を設定できないということが原因です。。
というわけで、IE6, 7にも対応したのが以下のコードです。 ポイントは、window のonload イベントが呼ばれたときに、id=downerのselect要素の子のoption要素に対して
cloneNode()メソッドを呼び出し、optionのクローンを作成していることです。
autoSelectOptions関数が呼ばれるたびに、一旦downerのoption要素を全て削除してから、複製化しておいたoptionから、条件に一致する要素のみ挿入しています。
サンプルコード
window.onload = function(){
var doc = document,
upper_select = document.getElementById("upper"),
downer_select = document.getElementById("downer"),
downer_options = downer_select.childNodes,
cloned_options = new Array();
//初期表示されるdownerのoptionのクローンを保存しておく
for( var i=0, len=downer_options.length; i < len; i++){
if (downer_options[i].tagName && downer_options[i].tagName.toLowerCase() === 'option') {
cloned_options.push(downer_options[i].cloneNode(true));
}
}
autoSelectOptions(upper_select, downer_select, cloned_options);
upper_select.onchange = function(){autoSelectOptions(upper_select, downer_select, cloned_options)};
}
function autoSelectOptions(key_select_box, target_select_box, option_list){
if (typeof key_select_box === 'undefined' ||
typeof target_select_box === 'undefined' ||
typeof option_list === 'undefined') {
return;
}
var first_node_flag = true,
ch = target_select_box.childNodes;
for (var i=ch.length - 1; i >= 0; i-- ){
//selectの子ノードを一旦すべて削除する。
ch[i].parentNode.removeChild(ch[i]);
}
var fragment = document.createDocumentFragment();
for (var i = 0, len = option_list.length; i < len; i++) {
if (option_list[i].tagName && option_list[i].tagName.toLowerCase() == 'option'){
option_list[i].selected = false;
if (option_list[i].value.split('_')[0] === key_select_box.value){
fragment.appendChild(option_list[i]);
if (first_node_flag) {
option_list[i].selected = true;//最初に挿入するノードを,選択状態にする
first_node_flag = false;
}
}
}
}
//documentに条件に一致するのoptionのみ追加
target_select_box.appendChild(fragment);
}
IE6, 7 ともに動作することを確認しました! 一旦option要素をすべてremoveするなどちょっと強引ですが、仕様は満たされてるので良しとします。
まとめ
- IE6, IE7 では、optionのdisabled属性は、設定できないので注意する!
参照URL
Forms in HTML documents
http://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#h-17.6
Node.cloneNode - MDN
https://developer.mozilla.org/En/DOM/Node.cloneNode