jQuery / Criar campos select dependentes
Campos select dependentes mudam as opções do segundo conforme a primeira seleção. Escute change do pai, esvazie o filho e acrescente os dados correspondentes. Reinicie também o valor antigo para não manter um item de outro grupo.
Definir dados e funções
O exemplo guarda Group A e B e seus arrays localmente, sem servidor nem plugin extra. O valor a escolhe seu array. Separe label visível e value enviado para que alterações de texto preservem o identificador estável.
Cada controle tem label associado e id único; name identifica os dados do formulário. O elemento select usa required para valor vazio, e o filho fica disabled sem itens. Controles desabilitados são omitidos, portanto o servidor também precisa conferir valores ausentes e combinações inválidas.
A opção vazia é orientação, não item real. Dê identificadores não vazios às opções reais para distinguir uma escolha deliberada.
Exemplo completo de listas relacionadas
Salve e abra dependent-select-demo.html em UTF-8. A internet carrega jQuery completo 4.0.0; não requer servidor nem compilação. Windows Edge com 4.0.0 e 3.7.1 verificou teclado, restauração, grupos vazios/desconhecidos, inspeção de pares e mudanças repetidas.
<!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>
No início não há seleção, existem zero opções reais além da orientação e o filho está desabilitado.
group=(none)
item=(none)
options=0; disabled=true
changes=0
- Group A mostra Choose an item, A-1 e A < B & C: dois itens reais.
- A-1 e Inspect selection exibem Valid pair: {"group":"a","item":"a1"}. Nada é enviado para fora.
- Group B troca para B-1 e B-2 e restaura a orientação vazia. a1 não deve permanecer.
- Sem escolher o filho, required bloqueia antes de submit. A mensagem depende do navegador e idioma.
- Group without items desabilita o filho com No available items. A inspeção pede um par disponível.
- Reset restaura o início. Confira também a seleção com setas do teclado.
changes conta execuções dos manipuladores change, não opções, cliques ou pessoas. Inicialização e Reset deixam zero; um evento programado também conta.
Reconstrução e eventos
.on("change", ...) conecta os controles existentes. O valor pai busca um array Map; chave ausente resulta em array vazio. Depois de esvaziar e acrescentar orientação e itens, .val("") remove a escolha antiga. Alternâncias A/B não acumulam opções.
new Option(label, value) cria texto sem concatenar HTML. A < B & C permanece literal. O valor lido é a1 ou a2, não necessariamente o texto visível.
Definir .val() não dispara change automaticamente. Para outra função escolher Group B, execute isto depois de inicializar e reconstrua o filho pelo mesmo caminho.
$("#group").val("b").trigger("change");
rebuildItem é privado dentro de ready. Use eventos ou projete uma API pública adequada. Se o select inteiro for substituído, reveja eventos diretos; delegar de um pai estável pode ajudar.
Reset chama form.reset() e recalcula o filho. A restauração nativa não reconstrói opções criadas por JavaScript. O id do botão é restore para não ocultar o método do formulário com um controle chamado reset.
Validar pares e ampliar
O manipulador submit impede o envio normal e confere associação ao array atual. required trata vazios habilitados, mas um filho disabled de grupo vazio fica fora da validação nativa. Formulário HTML válido e par válido para o negócio são condições diferentes.
FormData usa valores atuais dos controles com name e omite o filho desabilitado. É uma demonstração do navegador. Um servidor real precisa validar independentemente a relação permitida usando seus dados.
Com três níveis, mudar o primeiro apaga listas e valores do segundo e terceiro. O terceiro só é preenchido após um segundo válido. Para restaurar valores: dados pai, seleção pai, geração filha e valor filho válido.
Carregamento remoto exige lógica extra: limpar valores durante espera, distinguir carregando/erro/vazio e cancelar ou identificar solicitações para uma resposta A lenta não sobrescrever B. Aqui não há rede nem controle de concorrência implementados.
Se não mudar, confira IDs e valores reais, ordem e Console. Para valor antigo persistente, examine a sequência de opções e .val(). Revise labels, interface móvel nativa e textos longos sem forçar foco no próximo campo.




