JavaScriptで連動するプルダウンを選択した際に表示/非表示を切り替える
2年ほど前に「JavaScriptでプルダウンメニューを複数連動させるなるべく簡単な方法」という記事を書いたのですが、Googleの検索結果で上のほうに表示されるためかそこそこアクセス数があります。
(といっても月に400人くらい)
その記事で、うちのブログにしてはめずらしくコメントを頂いたので、はりきって回答してみたのですが、微妙に的外れな回答になってしまったようです。
コメントいわく、JavaScriptでのプルダウン選択時に要素の表示/非表示を切り替える方法が知りたいとのこと。
記事中ではOS一覧が表示される1段目のプルダウンとバージョン一覧が表示される2段目のプルダウンがあり、プルダウン1が変更されるたびにプルダウン2の中身をごっそりと入れ替えることによって連動を実現していました。
それを表示/非表示で切り替えたい、というのだから、2段目のプルダウンを非表示状態で複数列挙しておき、1段目のプルダウン変更時に連動して表示状態を切り替える方法、と思って回答したのですが、どうやらそういうことがしたいのではないようです。
プルダウンメニューで1段階目をクリック後に見えなくしたうえで、2段階目を選択するような機能を考えておりました
………!?
選択したプルダウンを消す……だと……!? そんな不可思議なUIではユーザーがびっくりしてしまうのではないかと思うのですが、プルダウンを消した後にラベル等を残す予定なのか、それとも選んだ内容をどこかにコピーして表示するのか…。何か深い事情があるのかも知れません。
ともあれ、ちょっと面白そうなので、プルダウンを選択したら、選択したプルダウンは消えて、下層プルダウンが現れるという不思議UIを作ってみることにしました。
プルダウンで選択するデータはツリー構造
まず前提となるデータを用意しなければなりません。前回はOSプルダウン、バージョンプルダウンで連動プルダウンを作成しましたが、今回はせっかくなので、もうちょい複雑に3階層くらいにしてみましょう。
都道府県とか、Amazonの商品分類とか、あるいは職種分類とか、どんなサンプルデータにしようか悩んだのですが、だんだん考えるのが面倒くさくなってきて結局記号にしてしまいました。センスなくてすみません…。
- A
- A-1
- A-1-1
- A-1-2
- A-1-3
- A-2
- A-2-1
- A-2-2
- A-2-3
- A-3
- A-3-1
- A-3-2
- A-3-3
- A-1
- B
- B-1
- B-1-1
- B-1-2
- B-1-3
- B-2
- B-2-1
- B-2-2
- B-2-3
- B-3
- B-3-1
- B-3-2
- B-3-3
- B-1
- C
- C-1
- C-1-1
- C-1-2
- C-1-3
- C-2
- C-2-1
- C-2-2
- C-2-3
- C-3
- C-3-1
- C-3-2
- C-3-3
- C-1
大分類がA、B、Cの3つで、その下層に3つずつ中分類と小分類が並ぶ感じですね。
これをプルダウンで選択するようにしてみましょう。
選択したプルダウンを消して下層プルダウンを選択させるサンプル
実行例
何はともあれ、まずは実行例です。
………………………自分で作っておいて何ですが、すげぇわかりづらい…。
プルダウンを選択した瞬間、そのプルダウンは消えて次のプルダウンが表示されるのですが、あまりに一瞬なので切り替わっているのがわかりづらいですよね。
やはり、実運用する際は選択した値をどこかに別枠で表示したほうが良さそうです。
HTML部分
単にプルダウンを並べているだけなので、クソ長いですが、やってることは単純です。
<div class="selectbox">
<select name="top">
<option value="">選択</option>
<option value="op-a">A</option>
<option value="op-b">B</option>
<option value="op-c">C</option>
</select>
<select name="op-a">
<option value="">Aグループから選択</option>
<option value="op-a-1">A-1</option>
<option value="op-a-2">A-2</option>
<option value="op-a-3">A-3</option>
</select>
<select name="op-a-1">
<option value="">A-1グループから選択</option>
<option value="op-a-1-1">A-1-1</option>
<option value="op-a-1-2">A-1-2</option>
<option value="op-a-1-3">A-1-3</option>
</select>
<select name="op-a-2">
<option value="">A-2グループから選択</option>
<option value="op-a-2-1">A-2-1</option>
<option value="op-a-2-2">A-2-2</option>
<option value="op-a-2-3">A-2-3</option>
</select>
<select name="op-a-3">
<option value="">A-3グループから選択</option>
<option value="op-a-3-1">A-3-1</option>
<option value="op-a-3-2">A-3-2</option>
<option value="op-a-3-3">A-3-3</option>
</select>
<select name="op-b">
<option value="">Bグループから選択</option>
<option value="op-b-1">B-1</option>
<option value="op-b-2">B-2</option>
<option value="op-b-3">B-3</option>
</select>
<select name="op-b-1">
<option value="">B-1グループから選択</option>
<option value="op-b-1-1">B-1-1</option>
<option value="op-b-1-2">B-1-2</option>
<option value="op-b-1-3">B-1-3</option>
</select>
<select name="op-b-2">
<option value="">B-2グループから選択</option>
<option value="op-b-2-1">B-2-1</option>
<option value="op-b-2-2">B-2-2</option>
<option value="op-b-2-3">B-2-3</option>
</select>
<select name="op-b-3">
<option value="">B-3グループから選択</option>
<option value="op-b-3-1">B-3-1</option>
<option value="op-b-3-2">B-3-2</option>
<option value="op-b-3-3">B-3-3</option>
</select>
<select name="op-c">
<option value="">Cグループから選択</option>
<option value="op-c-1">C-1</option>
<option value="op-c-2">C-2</option>
<option value="op-c-3">C-3</option>
</select>
<select name="op-c-1">
<option value="">C-1グループから選択</option>
<option value="op-c-1-1">C-1-1</option>
<option value="op-c-1-2">C-1-2</option>
<option value="op-c-1-3">C-1-3</option>
</select>
<select name="op-c-2">
<option value="">C-2グループから選択</option>
<option value="op-c-2-1">C-2-1</option>
<option value="op-c-2-2">C-2-2</option>
<option value="op-c-2-3">C-2-3</option>
</select>
<select name="op-c-3">
<option value="">C-3グループから選択</option>
<option value="op-c-3-1">C-3-1</option>
<option value="op-c-3-2">C-3-2</option>
<option value="op-c-3-3">C-3-3</option>
</select>
</div>
<button type="button" id="selectbox-reset">リセット</button>
<style>
.selectbox select:not(select[name=top]) {
display:none;
}
</style>
スタイルシートではname="top"以外のselectタグを非表示にしています。
JavaScript部分
<script>
window.addEventListener('load', function () {
//onchangeイベントの設定
document.querySelectorAll('.selectbox select').forEach(elm => {
elm.onchange = function () {
let elm2 = document.getElementsByName(this.value)[0];
if (elm2) {
elm.style.display = 'none';
elm2.style.display = 'block';
}
}
});
//リセットボタン
document.getElementById('selectbox-reset').onclick = function () {
document.querySelectorAll('.selectbox select').forEach(elm => {
if (elm.name == 'top') {
elm.style.display = 'block';
} else {
elm.style.display = 'none';
}
elm.selectedIndex = 0;
});
};
});
</script>
JavaScript部分は案外シンプルな作りでしょう?
解説
まず、スタイルシートによって、クラス名selectboxの中にあるselectタグはname=topを除いて全て非表示になっています。
そして、すべてのselectタグのonchange属性に下記のコードを埋め込むことで、自身の非表示と下層の表示を実現しています。
let elm2 = document.getElementsByName(this.value)[0];
if (elm2) {
elm.style.display = 'none';
elm2.style.display = 'block';
}
ここで重要なのはoptionタグのvalue属性には下層の名前を付けている点ですね。最上位のselectタグのoptionタグにはop-a/op-b/op-cという値がセットされており、同じ名前のselectタグが下層用として用意してあります。
なので、onchangeイベントが発生した際に自身のvalueでgetElementsByNameを呼び出せば、下層のプルダウンがすぐ見つけられるという仕組み。
このような作りにしておけば、いくらツリー構造を深く深くしていってもJavaScriptのコードは1ミリも変更する必要がありません。
あとリセットボタンの挙動については………詳しく説明するまでもない気もしますが………selectタグで検索して、すべてのプルダウンの選択(selectedIndex)を1番上にリセットし、name="top"の要素を表示状態に、それ以外を非表示状態にしているだけです。
まとめ
JavaScript関連でのコメントが珍しくて嬉しかったので、つい調子にのって記事にしてしまいましたが、正直需要があるのかどうかよくわかりません。
ただ、先述したとおり、「JavaScriptでプルダウンメニューを複数連動させるなるべく簡単な方法」という記事はそこそこ読まれていますし、「簡単なJavaScriptとCSSで横並びプルダウンを自作する」もたまに読まれているみたいです。
このブログは雑記ブログなのですが、Google的にはJavaScript系のブログと判定されて上位表示されているのかも知れません。謎です。
いずれにせよ、この記事のコードがどこかの誰かの参考になれば幸いです。