SELECT要素の複数連動。その2(iOS Safari(iPad, iPhone, iTouch)で動作可)jQueryなんて使わない。Ajaxも使わない

ちょっと改良した。

  1. option,optgroup は、htmlで定義した順に表示されるようにした。
  2. option,optgroup は、混在できる。
  3. optgroup を指定する value値を拡張できるようにした
    • カンマで区切るとoptgroupを複数指定できる。
    • * を指定するとすべて表示する
    • /^team[ad]$/ig などのように正規表現で利用出きるようにした。(文字によっては文法違反か?)
  4. selectedの指定は、たぶんマルチにも対応すると思う。前回のclass="selected"は中止。
  5. 個別のFORMに対応させようとも考えたが、classを同じにしなければ区別で切るのでやめた。
  6. classは、空白で区切り複数で切るので区別で切るのでSELECTの個別指定もやめた。
  7. 今後のためにちょっとコメントを書き加えた

それにしてもオブジェクト化するというのは、便利だな。

<!DOCTYPE html>
<meta charset="utf-8">
<title>SELECT要素の連動</title>
<style>
body {
  color: white;
  background: black;
}
</style>

<body>
<form id="SELECT_TEST">
<p>
  <label>Group:</label>
  <select name="group" class="selectA">
    <option value="GroupA">GROUP A</option>
    <option value="GroupB">GROUP B</option>
  </select>

  <label>Team:</label>
  <select name="team" class="selectA">
    <optgroup label="GroupA">
      <option value="TeamA">TEAM A</option>
      <option value="TeamB">TEAM B</option>
    </optgroup>
    <optgroup label="GroupB">
      <option value="TeamC">TEAM C</option>
      <option value="TeamD">TEAM D</option>
    </optgroup>
  </select>

  <label>Player:</label>
  <select name="player" class="selectA">
    <optgroup label="TeamA">
      <option value="PlayerA">PLAYER A</option>
      <option value="PlayerB">PLAYER B</option>
    </optgroup>
    <optgroup label="TeamB">
      <option value="PlayerC">PLAYER C</option>
      <option value="PlayerD">PLAYER D</option>
    </optgroup>
    <optgroup label="TeamC">
      <option value="PlayerE">PLAYER E</option>
      <option value="PlayerF">PLAYER F</option>
    </optgroup>
    <optgroup label="TeamD">
      <option value="PlayerG">PLAYER G</option>
      <option value="PlayerH">PLAYER H</option>
    </optgroup>
  </select>
</p>


<p>
  <label>Group:</label>
  <select name="group" class="selectB">
    <option value="GroupA">GROUP A</option>
    <option value="GroupB" selected>GROUP B</option>
    <option value="Free">PLAYER Free</option>
  </select>

  <label>Team:</label>
  <select name="team" class="selectB">
    <optgroup label="GroupA">
      <option value="TeamA">TEAM A</option>
      <option value="TeamB">TEAM B</option>
    </optgroup>
    <optgroup label="GroupB">
      <option value="TeamC">TEAM C</option>
      <option value="TeamD   ,TeamC , TeamA" selected>TEAM D</option>
    </optgroup>
    <option value="/^f.*$/i">PLAYER Free</option>
    <option value="/^Team[AD]$/gi">Team A or D</option>
    <option value="*">Team ALL</option>
  </select>

  <label>Player:</label>
  <select name="player" class="selectB">
    <optgroup label="TeamA">
      <option value="PlayerA">PLAYER A</option>
      <option value="PlayerB">PLAYER B</option>
    </optgroup>
    <optgroup label="TeamB">
      <option value="PlayerC">PLAYER C</option>
      <option value="PlayerD">PLAYER D</option>
    </optgroup>
    <optgroup label="TeamC">
      <option value="PlayerE">PLAYER E</option>
      <option value="PlayerF" selected>PLAYER F</option>
    </optgroup>
    <optgroup label="TeamD">
      <option value="PlayerG">PLAYER G</option>
      <option value="PlayerH" selected>PLAYER H</option>
      <option value="PlayerI" selected>PLAYER I</option>
      <option value="PlayerJ">PLAYER J</option>
    </optgroup>
    <optgroup label="Free">
      <option value="Player FreeX">PLAYER FREE X</option>
      <option value="Player FreeY">PLAYER FREE Y</option>
    </optgroup>
  </select>
</p>

<script>

(function (D) {

  function toAry (nodeList) {//ノードリストを配列
    return Array.prototype.slice.call (nodeList);
  }

  function getVal (select) {//SELECT値の取得
    return select.options[select.selectedIndex].value;
  }

  function setSelected (e) {//selectedの設定
    e.selected = !! this;
  }

  function delChild (e) {//子要素を削除する
    while (e.hasChildNodes ())
      e.removeChild (e.firstChild);
  }
  
  //______
  
  //子要素は、表示用とそのクローンが対になる
  function ESelect (select) {
    this.eSelect = select;//b:表示ようにある
    this.cSelect = select.cloneNode (true);//a:クローン
  }

  //親のSELECTのValue値を子のoptgroupのラベルとして検索させ表示する
  function replaceGroup (label) {
    var i, e, mode, s;
    var a = this.cSelect, b = this.eSelect; //a -> b
    var c = a.childNodes;
    var ary = [ ], reg, f;

    switch (true) {
    case 'string' === typeof label :
      switch (true) {
      case (f = /^\/(.+)\/([gmi]*)?$/.exec (label), Array.isArray (f)) :
        reg = new RegExp (f[1],f[2] || null); mode = 3;
        break; //正規表現として処理
      case /^.+,.+$/.test (label) :
        ary = label.split (/\s*,\s*/g); mode = 2;
        break; //配列として処理
      case '*' === label : mode = 0; break;
      default : mode = 1; break; //文字列として処理
      }
      break;
    default:
      mode = 0; //すべてコピーする
      break;
    }
    delChild (b);//表示用を削除
    for (i = 0; e = c[i]; i += 1) {
      if ('OPTGROUP' === e.tagName) {
        switch (mode) {
        case 1 : if (e.label !== label) continue; break;//文字列として処理
        case 2 : if (-1 === ary.indexOf (e.label)) continue; ;break;//配列として処理
        case 3 : if (! reg.test (e.label)) continue; break;//正規表現として処理
        case 0: default : break;//すべてコピーする
        }
      }      
      b.appendChild (e.cloneNode (true));//コピーして表示用のSELECTに追加する
    }
    
    toAry (b.querySelectorAll ('option[selected]'))
      .forEach (setSelected, true);

  }

  //______
  
  //内部での呼び出し(子SELECTをオブジェクト化して呼び出すため)
  ESelect.prototype.replaceGroup = replaceGroup;

  //______


  //イベント処理
  function selectChangeHandle (event) {
    var e = event.target;
    var n, t;

    //親SELECT配列に存在すればvalue値に対応した子のグループを表示させる
    while (-1 < (n = this.selectBuffer.indexOf (e))) {
      t = this.cloneBuffer[n];
      t.replaceGroup (getVal (e));
      e = t.eSelect;//子を親にして孫に対応する
    }
  }


  //SELECT要素に対して初期設定する
  function init (select) {
    var buf = { }, begin = [ ];
    var i, s, e, n;

    for (i = 0; s = select[i]; i += 1) {
      if ((n = s.className)) {//クラスがしていされていたら
        if (e = buf[n]) {
          e.addEventListener ('change', this, false);//イベントを貼り付け
          this.selectBuffer.push (e);//親のSELECT要素を配列に
          this.cloneBuffer.push (new ESelect (s));//子のSELECT要素を配列にして親と対応する
          buf[n] = s;//孫に備える
        }
        else
          begin.push (buf[n] = s); //一時保存して2度目の出現に備える
      }
    }

    begin.forEach (function (select) {
      selectChangeHandle.call (this, { target: select });
    }, this);
  }

  //______

  var Interlock = new Object;

  Interlock.handleEvent  = selectChangeHandle;
  Interlock.selectBuffer = [ ];
  Interlock.cloneBuffer  = [ ];

  init.call (Interlock, D.querySelectorAll ('select'));
}) (document);

</script>
(function(l){function m(a){a.selected=!!this}function h(a){this.b=a;this.f=a.cloneNode(!0)}function k(a){a=a.target;for(var b;-1<(b=this.c.indexOf(a));)b=this.a[b],b.g(a.options[a.selectedIndex].value),a=b.b}h.prototype.g=function(a){var b,f,e,c=this.b,n=this.f.childNodes,g=[],d;switch(!0){case "string"===typeof a:switch(!0){case b=/^\/(.+)\/([gmi]*)?$/.exec(a),Array.isArray(b):d=new RegExp(b[1],b[2]||null);e=3;break;case /^.+,.+$/.test(a):g=a.split(/\s*,\s*/g);e=2;break;case "*"===a:e=0;break;default:e=
1}break;default:e=0}for(;c.hasChildNodes();)c.removeChild(c.firstChild);for(b=0;f=n[b];b+=1){if("OPTGROUP"===f.tagName)switch(e){case 1:if(f.label!==a)continue;break;case 2:if(-1===g.indexOf(f.label))continue;break;case 3:if(!d.test(f.label))continue}c.appendChild(f.cloneNode(!0))}Array.prototype.slice.call(c.querySelectorAll("option[selected]")).forEach(m,!0)};var d={};d.handleEvent=k;d.c=[];d.a=[];(function(a){var b={},f=[],e,c,d,g;for(e=0;c=a[e];e+=1)if(g=c.className)(d=b[g])?(d.addEventListener("change",
this,!1),this.c.push(d),this.a.push(new h(c)),b[g]=c):f.push(b[g]=c);f.forEach(function(a){k.call(this,{target:a})},this)}).call(d,l.querySelectorAll("select"))})(document);