2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

セレクトボックス内で既存に選択された項目を選択できなくする方法

Posted at

背景

共同開発でRailsを使って野球のスコアブックアプリの制作を行っております。
野球のポジション選択をアクティブハッシュを用いたセレクトボックスで行っているのですが、
既存に選択した項目を選択できなくすることはできないかという要望がありました。

そこで、面白い発想だと感じまして試験的に作成してみることにしました。

完成イメージ図

Image from Gyazo

機能を実装する為の考え方

まず変更パターンが3つあると考えられます
 1.初期値--から値を選択
 2.現在の選択から初期値--を選択
 3.現在の選択から初期値以外の値を選択

ActiveHashにおいて、下記のような構造を取っています。
{ id: 1, name: '--' },
{ id: 2, name: '投' },
{ id: 3, name: '捕' },
optionタグのvalue値がidであり、表示値がnameとなります。
selectタグのvalue値は選択中のoptionタグのvalue値となります。

このことを踏まえて、3つ想定される挙動について考えてみることにしました。

1.初期値--から選択
  1. 値を選択
  2. selectのvalueは1で選択した値となります。
  3. 他のselectタグのvalueについて、2で選択した値を選択出来なくします。
2.現在の選択から初期値--を選択
  1. 値を選択
  2. selectのvalueは1で選択した値となります。
  3. 他のselectタグのvalueについて、1での変更前の値を選択出来るようにします。
3.現在の選択から初期値以外の値を選択
  1. 値を選択
  2. selectのvalueは1で選択した値となります。
  3. 他のselectタグのvalueについて、1での変更前の値を選択出来るように、2で変更後の値を選択出来ないようにします。
挙動のまとめと考察

・ 現在選択している値はselectのvalue値で分かる。
・ 現状は前に選択していた値は分からない。
  => 判定する為の何かの基準を設ける必要がある。
・ 初期値に関しては、他のselectタグ以外に影響を及ぼすことがない。

以上の情報を基に前回の入力値にはクラス名を付与する方針の上、実装手順を考える。

実装手順

  1. selectタグのHTML要素を取得
  2. selectタグの子のHTML要素(=option)の数を事前取得
  3. 選択した項目の値が変化した時のイベントを全selectタグに設置
  4. イベント発火後、選択している現在の値を変数に格納
  5. 子要素(optionタグ)の中で前回の選択値の証明書となるクラス名pre-valueを持つものを探す
  6. 当てはまる要素があれば、値を前回の値として変数に格納後、クラス名を除去
  7. 現在の値があるoptionタグにクラス名pre-valueを付与
  8. slectタグを変更した箇所以外のselectタグについて、4で格納した新規に設定された値のoptionタグに関しては、disabled属性を付与。
  9. 6で格納した前回の値に当てはまるoptionタグはdisabled属性を除去する。
reduce_position.js

function reduceOption() {
  //子であるoptionタグの構成要素は全て同じなので、selectを親とした子の要素数も同じ
  const positionSelect = document.querySelectorAll(".input-game-defensive");
  const SelectLength = positionSelect.length;
  const OptionLength = positionSelect[0].children.length;
  
  
  for(let i=0; i<=SelectLength - 1; i++){
    positionSelect[i].addEventListener("change", (e) => {
      const targetValue = positionSelect[i].value;
      const targetChildren = positionSelect[i].children;
      //前回の初期値としてタグ上での初期値--のvalue値1を付与
      let preValue = 1;
      for(let c=0; c<=OptionLength-1;c++){
        if(targetChildren[c].classList.contains("pre-value")){
          preValue = targetChildren[c].value;
          targetChildren[c].classList.remove("pre-value");
        }
      }
      //全てのselectタグのHTML要素に処理を行う。
      targetChildren[targetValue - 1].classList.add("pre-value");
      for(let k=0; k<=SelectLength - 1; k++){
        //変更したセレクトタグの要素数i、preValue値1の初期値--以外で処理を実行する。
        if(preValue != 1 && k!=i){
          //前回の値が入っていた無効の属性を削除
          positionSelect[k].children[preValue - 1].removeAttribute("disabled");
        }
        if(targetValue != 1 && k!=i){
          //新規に指定された値の属性を無効化
          positionSelect[k].children[targetValue - 1].setAttribute("disabled", true);
        }
      }
    });
  };
}
window.addEventListener("load", reduceOption);

補足情報

.children
現在マッチしている要素の子要素を取得します。
親要素.children[n]で指定した親に対して、n番目の子であるHTML要素を指定することが出来ます。

.classList.add, .classList.remove
クラス名の追加と削除が出来るメソッド

setAttribute("disabled", true)
disabledは無効を示す属性で、無効の効果がtrueかfalseで設定出来ます。
今回は無効にしたいので第二引数をtrueで記載してます。

最後に

最後まで見てくださりありがとうございます。
機能を実装するまでの考え方について記載したので長くなったかと存じます。

簡単にまとめますと、
現在の選択値に当てはまるoptionタグがクラス名pre-valueを持つことで、
変更後と変更前の値を識別出来るようになり望んだ機能を実装出来ました。

少し工夫をすれば出来ないことが出来るようになると信じて、
これからも実装を行って参ります。

2
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?