🏄‍♂️

ラジオボタン風チェックボックスを作る

2022/08/07に公開

コードレビューを受けられる環境がなく、jsに関しては独学です。
改善すべき箇所があればご指摘いただけると幸いです。

選択を外せるラジオボタンを作る

  • 同時に選択させたくない
  • 選択を外せるようにしたい

想定される実装方法

  • ラジオボタンをもとに、チェックを外せるようにする
  • チェックボックスをもとに、同時に選択をさせないようにする
    の2パターンかと思います。
    元々はチェックボックスで構築してあったので、今回は後者を選択しました。

APIを叩いて挿入したDOMに対しても処理を行いたいけど、
ページロード時にあるDOMに対してイベントを重複して付与したくなかったので
第一引数にDOMを渡すことで、特定のDOMツリーの中にだけイベントを付与をできるクラスを作成しました。

デモ

実際は見た目もラジオボタンっぽく実装しましたが、
チェックボックスで作ったっていうのがわかるようにデモではスタイルをいじってません。

コードの大まかな流れ

  1. 特定のDOMツリーの中からclass="js-single"をもつ要素を取得
  2. 1取得した要素にクリックイベントを付与
  3. クリックされた要素のname属性をnameに保持
  4. クリックされた要素がチェックされたのか、外されたのかをstateに保持
  5. 3で保持されたものと同じnameをもつ要素を探して、(クリックされた要素も含めて)全部のチェックを外す
  6. 3で保持されたstateがtrueならクリックされた要素にstateを付与する

コード全体

window.addEventListener('DOMContentLoaded', () => {
  new SingleCheck();
})

 class SingleCheck {
  constructor(scope) {
    this.DOM = {};
    this.DOM.scope = scope ? scope : document;
    this.DOM.targets = this.DOM.scope.querySelectorAll(".js-single");
    this.#addEvent();
  }

  #singleSelect(el) {
    const name = el.name;
    const state = el.checked;
    const $sameNameEls = this.DOM.scope.querySelectorAll(`[name="${name}"]`);

    $sameNameEls.forEach(el => {
      el.checked = false;
    })

    if(state) {
      el.checked = state;
    }
  }

  #addEvent() {
    const that = this;

    this.DOM.targets.forEach(target => {
      target.addEventListener('click', (e) => {
        const el = e.currentTarget;
        that.#singleSelect(el);
      })
    })
  }
}

コードの説明

1.class="js-single"をもつ要素を取得

constructorの最初の3行で行っています。
DOMツリーを指定しない場合はdocumentの指定したことにします。

 constructor(scope) {
    this.DOM = {};
    this.DOM.scope = scope ? scope : document;
    this.DOM.targets = this.DOM.scope.querySelectorAll(".js-single");
  }

2.クリックイベントを付与

constructorの最初の4行目で#addEvent()を呼ぶ。
#addEvent内では要素全部をクリックイベントを付与

 constructor(scope) {
    this.#addEvent();
  }
  
  #addEvent() {
    const that = this;

    this.DOM.targets.forEach(target => {
      target.addEventListener('click', (e) => {
        const el = e.currentTarget;
        that.#singleSelect(el);
      })
    })
  }

3~6チェックを外して、クリックされた要素に状態を付与

  1. クリックされた要素のname属性をnameに保持
  2. クリックされた要素がチェックされたのか、外されたのかをstateに保持
  3. 3で保持されたものと同じnameをもつ要素を探して、(クリックされた要素も含めて)全部のチェックを外す
  4. 3で保持されたstateがtrueならクリックされた要素にstateを付与する
 #singleSelect(el) {
    const name = el.name; //3.クリックされた要素のname属性を保持
    const state = el.checked; // 4. クリックされた要素がチェックされたのか、外されたのかを保持
    //↓5. 3で保持されたものと同じnameをもつ要素を取得
    const $sameNameEls = this.DOM.scope.querySelectorAll(`[name="${name}"]`); 

    //↓5. (クリックされた要素も含めて)全部のチェックを外す
    $sameNameEls.forEach(el => {
      el.checked = false;
    })

       //↓6. 持されたstateがtrueならクリックされた要素にstateを付与する
    if(state) {
      el.checked = state;
    }
  }

好きなタイミングでインスタンス化すればいつでも使い回せるかと思います!
#addEventの中身もっといい書き方があればご教授いただけると幸いです。

最後まで読んで頂きありがとうございました!

Discussion