jQuery / Abhängige select-Felder erstellen

Abhängige select-Felder ändern ihre Unteroptionen gemäß der vorherigen Auswahl. Reagieren Sie auf change des Elternfelds, leeren Sie das Kind und setzen Sie passende Optionen ein. Löschen Sie auch dessen alten Wert, damit kein Eintrag der vorherigen Gruppe stehen bleibt.

Daten und Rollen festlegen

Group A und B besitzen lokale Eintragsarrays ohne Server oder Zusatzplugin. Elternwert a wählt sein Array. Trennen Sie sichtbares label vom übermittelten value, damit Textänderungen den stabilen Schlüssel nicht ändern.

Jedes Feld hat passendes label, eindeutige id und für Übermittlung einen name. Beim select-Element prüft required den leeren Wert; ohne Einträge wird das Kind disabled. Deaktivierte Felder fehlen in Formulardaten, deshalb müssen fehlende Werte und ungültige Paare serverseitig geprüft werden.

Die leere Option ist eine Anleitung, kein echter Eintrag. Verwenden Sie für echte Optionen nichtleere Schlüssel zur Unterscheidung.

Vollständiges Beispiel

Speichern und öffnen Sie UTF-8 als dependent-select-demo.html. Internet lädt vollständiges jQuery 4.0.0; Server und Build sind unnötig. Windows Edge mit 4.0.0 und 3.7.1 prüfte Tastatur, Zurücksetzen, leere/unbekannte Gruppen, Paaranzeige und wiederholte Wechsel.

<!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>

Anfangs sind beide Werte ungewählt, es gibt null echte Optionen und das Kind ist deaktiviert.

group=(none)
item=(none)
options=0; disabled=true
changes=0
  1. Group A liefert Choose an item, A-1 und A < B & C, also zwei echte Einträge.
  2. A-1 und Inspect selection melden Valid pair: {"group":"a","item":"a1"}. Es erfolgt keine externe Übermittlung.
  3. Group B ersetzt die Einträge durch B-1 und B-2 und stellt den leeren Platzhalter her. a1 darf nicht bleiben.
  4. Ohne Kinderauswahl blockiert required vor dem submit-Event; die Meldung hängt von Browser und Sprache ab.
  5. Group without items deaktiviert das Kind mit No available items. Die Prüfung verlangt ein verfügbares Paar.
  6. Reset stellt den Anfang her. Prüfen Sie auch die Auswahl per Pfeiltasten.

changes zählt die Ausführungen der change-Handler, nicht Optionen, Klicks oder Personen. Anfang und Reset setzen null; auch ein programmiertes change zählt.

Neuaufbau und Ereignisse

.on("change", ...) bindet die beiden vorhandenen Felder. Der Elternwert sucht ein Map-Array; fehlende Schlüssel liefern ein leeres Array. Nach Leeren, Platzhalter und neuen Optionen entfernt .val("") die alte Auswahl. Wiederholtes A/B häuft keine Optionen an.

new Option(label, value) erstellt Text statt verkettetes HTML. A < B & C bleibt wörtlich. Gelesen wird der Schlüssel a1 oder a2, nicht zwingend die sichtbare Beschriftung.

Ein .val()-Setter löst change nicht automatisch aus. Soll eine andere Funktion Group B wählen, führen Sie nach Initialisierung Folgendes aus, damit auch die Unterliste neu entsteht.

$("#group").val("b").trigger("change");

rebuildItem liegt privat im ready-Callback. Andere Funktionen können Ereignisse verwenden oder eine passende öffentliche API erhalten. Wird das select selbst ersetzt, sind direkte Handler neu zu bedenken; Delegation von einem beständigen Elternknoten kann helfen.

Reset ruft form.reset() auf und berechnet die Kindliste neu. Natives Reset rekonstruiert JavaScript-Optionen nicht. Die Schaltfläche heißt per id restore, damit ein reset-namiges Feld nicht die Formularmethode verdeckt.

Paare prüfen und erweitern

Der submit-Handler verhindert Standardübermittlung und prüft Zugehörigkeit zum aktuellen Array. required behandelt leere aktive Felder, ein deaktiviertes Kind einer leeren Gruppe wird jedoch nicht nativ validiert. HTML-Gültigkeit und fachlich gültiges Paar sind verschieden.

FormData verwendet aktuelle Werte benannter Felder und lässt das deaktivierte Kind aus. Dies ist eine Browserübung. Ein echter Server muss die erlaubte Beziehung anhand eigener Daten unabhängig prüfen.

Bei drei Ebenen löscht eine Änderung der ersten sowohl zweite als auch dritte Liste und Werte. Erst eine gültige zweite Auswahl füllt die dritte. Gespeicherte Werte werden in Reihenfolge Elterndaten, Elternwert, Kindaufbau und gültiger Kindwert wiederhergestellt.

Asynchrones Laden braucht zusätzliche Logik: alte Werte während des Wartens löschen, Laden/Fehler/Leer unterscheiden und alte Anfragen abbrechen oder identifizieren, damit eine späte A-Antwort B nicht überschreibt. Diese Übung implementiert weder Netzwerk noch solche Konkurrenzbehandlung.

Ohne Änderung prüfen Sie tatsächliche IDs/Werte, Reihenfolge und Console. Alte Werte erfordern Prüfung der Reihenfolge von Optionen und .val(). Kontrollieren Sie label, mobile Standardbedienung und lange Texte und erzwingen Sie keinen Fokuswechsel.