2011年12月15日木曜日

[JavaScript]option要素にdisabledを設定する

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

0 件のコメント:

コメントを投稿