jQuery / 連動するselectを作る方法
連動するselectは、先に選んだ値に応じて次のselectのoptionを変える構成です。親のchangeを検知し、子を空にして対応データを入れ直します。以前の子の値も解除し、前のグループの項目が残らないようにします。
データと役割を決める
Group A・Bと各項目をローカル配列に持ち、サーバーや追加プラグインは使いません。親valueがaなら対応配列を読みます。表示labelと送信用valueを分けると、文言変更でも識別子を保てます。
各要素に対応するlabel、一意のid、送信用nameを付けます。select要素のrequiredは空値の検査に使い、項目がなければ子をdisabledにします。無効な要素は送信データから除かれるため、サーバー側では欠落と不正な組合せも確認します。
空のoptionは案内で実項目ではありません。実データには空でない識別子を使い、意図した選択と区別します。
一覧を連動させる完全な例
UTF-8のdependent-select-demo.htmlとして保存し開きます。ネットから完全版jQuery4.0.0を読み、サーバーやビルドは不要です。Windows Edgeで4.0.0・3.7.1それぞれのキーボード、初期化、空・未知グループ、組合せ確認と反復切替を検証しました。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dependent select controls</title>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<style>
body { font: 16px/1.6 sans-serif; max-width: 720px; margin: 20px; }
label { display: block; margin-top: 12px; }
select, button { font: inherit; max-width: 100%; margin: 6px 0; }
pre { white-space: pre-wrap; overflow-wrap: anywhere; }
</style>
</head>
<body>
<form id="chooser">
<label for="group">Group</label>
<select id="group" name="group" required>
<option value="">Choose a group</option>
<option value="a">Group A</option>
<option value="b">Group B</option>
<option value="empty">Group without items</option>
</select>
<label for="item">Item</label>
<select id="item" name="item" required disabled><option value="">Choose a group first</option></select>
<button type="submit">Inspect selection</button>
<button type="button" id="restore">Reset</button>
</form>
<pre id="result" aria-live="polite"></pre>
<p id="submission" role="status">Nothing inspected.</p>
<script>
jQuery(function ($) {
const choices = new Map([
["a", [{value: "a1", label: "A-1"}, {value: "a2", label: "A < B & C"}]],
["b", [{value: "b1", label: "B-1"}, {value: "b2", label: "B-2"}]],
["empty", []]
]);
const $group = $("#group"), $item = $("#item"), form = document.querySelector("#chooser");
let changes = 0;
function render() {
$("#result").text("group=" + ($group.val() || "(none)") + "\nitem=" + ($item.val() || "(none)") +
"\noptions=" + Math.max(0, $item[0].options.length - 1) + "; disabled=" + $item.prop("disabled") + "\nchanges=" + changes);
}
function rebuildItem() {
const items = choices.get($group.val()) || [];
const prompt = items.length ? "Choose an item" : ($group.val() ? "No available items" : "Choose a group first");
$item.empty().append(new Option(prompt, ""));
for (const item of items) $item.append(new Option(item.label, item.value));
$item.val("").prop("disabled", items.length === 0);
$("#submission").text("Nothing inspected."); render();
}
$group.on("change", function () { changes++; rebuildItem(); });
$item.on("change", function () { changes++; $("#submission").text("Nothing inspected."); render(); });
$(form).on("submit", function (event) {
event.preventDefault();
const items = choices.get($group.val()) || [];
if (!items.some(item => item.value === $item.val())) {
$("#submission").text("Choose a group with an available item."); return;
}
$("#submission").text("Valid pair: " + JSON.stringify(Object.fromEntries(new FormData(form))));
});
$("#restore").on("click", function () { form.reset(); changes = 0; rebuildItem(); });
rebuildItem();
});
</script>
</body>
</html>
初期値は両方未選択、案内以外の項目0、子は無効です。
group=(none)
item=(none)
options=0; disabled=true
changes=0
- Group AでChoose an item、A-1、A < B & Cが出ます。実項目は二つです。
- A-1を選びInspect selectionでValid pair: {"group":"a","item":"a1"}となります。外部送信はしません。
- Group Bへ変えるとB-1・B-2となり、子は空案内へ戻ります。a1は残りません。
- 子を選ばず確認するとrequiredがsubmitイベントより先に止めます。案内文言はブラウザーと言語によります。
- Group without itemsでは子が無効となりNo available itemsです。確認時は有効な組合せを促します。
- Resetで初期状態へ戻し、方向キーでも同じ連動を確認します。
changesはchangeハンドラーの実行回数です。項目数・クリック数・利用人数ではありません。初期化とResetは0で、コードによるイベントも増加対象です。
再生成とchangeを理解する
.on("change", ...)は既存の二要素へ接続します。親の値からMap配列を探し、未知キーなら空配列にします。子を空にして案内と項目を追加し、.val("")で古い値を外します。A/Bを反復してもoptionは累積しません。
new Option(label, value)はHTML連結ではなく文字として作るため、A < B & Cもそのまま表示します。選択後の値は表示文言でなくa1・a2などの識別子です。
.val()の設定だけではchangeは自動発生しません。別機能からGroup Bを選ぶ場合、初期化後に次を実行して同じ経路で子も作り直します。
$("#group").val("b").trigger("change");
rebuildItemはready内部の非公開関数です。他のコードはイベントで連携するか公開APIを設計します。select自体を置換すると直接接続イベントも再検討が必要で、残る親からの委譲も選択肢です。
Resetはform.reset()後に子を再計算します。標準resetだけでJavaScript生成のoptionは再構築されません。ボタンidはrestoreとし、resetという名前でフォームのメソッドを隠す衝突を避けました。
組合せ検査と拡張
submitで通常送信を止め、現在グループの配列に子が含まれるか確認します。requiredは有効要素の空値には働きますが、空グループで子がdisabledなら標準検証から除外されます。HTML上の有効と業務上の有効な組合せは別です。
FormDataはname付き要素の現在valueを使い、disabledな子は省きます。これはブラウザー内の実習です。実サーバーは自身のデータで親子関係を再検証します。
三段階なら最初の変更で二番目と三番目の一覧・値を解除し、有効な二番目の選択後に三番目を作ります。保存値の復帰は親データ、親選択、子生成、有効な子選択の順です。
非同期取得では待機中に古い値を消し、読込・失敗・空を分けます。遅いA応答が新しいB一覧を上書きしないよう取消や要求識別が必要です。この例に通信や競合処理はありません。
変わらなければ実id・value・読込順・Consoleを確認します。古い値は生成と.val()の順序を点検します。label、実機の選択UI、長い文言も確認し、次の入力へ勝手にフォーカスを移しません。




