ラジオボタンみたいに1つの要素だけ選択する【JavaScript】

JavaScript

フォームのラジオボタンは、グループ内で1つの要素しか選択できません。
今回、同じ挙動をラジオボタン以外の要素で実装したのでメモ。

完成したラジオボタン風の動作を確認する(デモ)

動作デモ

今回、ブラウザの背景を切り替えるサンプルを作りました。
好きなパターンと色を指定できます。

実際の動作はこちらでご確認ください!(別ウィンドウが開きます)

ローカルストレージに値を保存しているので、タブを閉じたり、リロードしても選択したパターンが再現されます。また、プロキシで選択内容をリアルタイムで表示しています。
こちらについての解説はこちらを参考してください。

ソースコードを確認(全体)

実装に必要なコードだけ抜き出しました。

HTML

<div class="select_wrap pattern_item">
    <p class="ttl">パターンの種類</p>
    <div class="item">
        <p class="elm" data-background="Hideout">
            <img src="./images/hideout.svg" alt="Hideout">
        </p>
        <p class="elm" data-background="Charlie-Brown">
            <img src="./images/charlie-brown.svg" alt="Charlie-Brown">
        </p>
        <p class="elm" data-background="YYY">
            <img src="./images/yyy.svg" alt="YYY">
        </p>
        <p class="elm" data-background="Piano-Man">
            <img src="./images/piano-man.svg" alt="Piano-Man">
        </p>
        <p class="elm" data-background="Pie-Factory">
            <img src="./images/pie-factory.svg" alt="Pie-Factory">
        </p>
        <p class="elm" data-background="Connections">
            <img src="./images/connections.svg" alt="Connections">
        </p>
        <p class="elm" data-background="no-data">
            パターン<br>なし
        </p>
    </div>
</div>

<div class="select_wrap fill_color_item">
    <p class="ttl">パターンの色</p>
    <div class="item">
        <p class="elm" data-fillcolor="#ff237e"></p>
        <p class="elm" data-fillcolor="#508b94"></p>
        <p class="elm" data-fillcolor="#595ee6"></p>
        <p class="elm" data-fillcolor="#b5ac45"></p>
        <p class="elm" data-fillcolor="#ac75c3"></p>
        <p class="elm" data-fillcolor="#ffffff"></p>
        <p class="elm" data-fillcolor="#9C92AC"></p>
    </div>
</div>


<div class="select_wrap bg_color_item">
    <p class="ttl">背景色</p>
    <div class="item">
        <p class="elm" data-bgcolor="#ffcece"></p>
        <p class="elm" data-bgcolor="#bfedff"></p>
        <p class="elm" data-bgcolor="#f2f9b0"></p>
        <p class="elm" data-bgcolor="#cacaca"></p>
        <p class="elm" data-bgcolor="#e8d0ff"></p>
        <p class="elm" data-bgcolor="#fdeac1"></p>
        <p class="elm" data-bgcolor="#fff9e9"></p>
    </div>
</div>

css

.item > .elm.check_elm {
    border: 3px solid red;
    box-shadow: 0 0 8px #3a3a3a;
}

Javascript

//CSS変更
function changeCss(elm) {
    const parent = elm.parentNode;
    const child_nodes_count = parent.childElementCount;
    for(var i=0; i<child_nodes_count; i++) {
        const item = parent.children[i];
        item.classList.remove("check_elm");
    }
    elm.classList.add("check_elm");
}
// イベント登録
document.querySelectorAll(".elm").forEach(function(elm) {
    elm.addEventListener('click',function() {
        changeCss(this);
    });
});

HTML構造の確認

選択できる項目をPタグで作成し、各要素にはクラス名elmを設定しています。
そして、グループ化したい親要素にselect_wrapを設定しています。

CSSを確認

今回、選択された要素を赤色で囲ってます。
要素を赤色で囲うCSSのクラス名はcheck_elmです。javascriptで要素がクリックされた時、このクリック専用のクラス名を要素に追加します。

.item > .elm.check_elm {
    border: 3px solid red;
    box-shadow: 0 0 8px #3a3a3a;
}

Javascriptを確認

CSSを変更する関数を作成する

関数changeCssにCSSを変更する処理をまとめています。
引数elmにはクリックした要素が渡されます。
処理としては、次のような流れになります。

  • クリックした要素の親を取得
  • 同じグループの子要素の数を取得
  • for文で同じグループの子要素全てにアクセス
  • クリック専用のクラス名があれば削除
  • クリックした要素にクリック専用のクラス名を追加
function changeCss(elm) {
    const parent = elm.parentNode;
    const child_nodes_count = parent.childElementCount;
    for(var i=0; i<child_nodes_count; i++) {
        const item = parent.children[i];
        item.classList.remove("check_elm");
    }
    elm.classList.add("check_elm");
}

クリックした要素の親要素を取得する

クリックした要素と同じグループの要素を取得するには、まず共通の親となる要素を取得します。

//親要素を取得
const parent = elm.parentNode;

子要素にあるクリック専用のクラス名を削除する

親要素から同じグループの子要素が取得できます。
子要素には次の構文でアクセスします。

親要素.children[何番目];

子要素の数だけfor文で各要素にアクセスし、クリック専用のクラス名(check_elm)があれば削除します。

const parent = elm.parentNode;
const child_nodes_count = parent.childElementCount;
for(var i=0; i<child_nodes_count; i++) {
    const item = parent.children[i];
    item.classList.remove("check_elm");
}

クリック専用のクラス名を追加する

同じグループの子要素に対してクラス名を削除したら、クリックした要素にクリック専用のクラス名を追加します。

elm.classList.add("check_elm");

これで同じグループ内では1つの項目しか選択できなくなりました。

クリックイベントを設定する

最後にクリックイベントを設定すれば完了です!

// イベント登録
document.querySelectorAll(".elm").forEach(function(elm) {
    elm.addEventListener('click',function() {
        changeCss(this);
    });

まとめ

フォーム以外でもグループ内で1つだけ選択してもらいたいシーンはあると思います。
ラジオボタンをCSSで調整してソレっぽく見せるのも大変なので、今回はJavascriptで調整しました。