jQuery / Crear select dependientes
Los select dependientes cambian las opciones del segundo según la primera selección. Escuche change del padre, vacíe el hijo y agregue los datos correspondientes. Reinicie también el valor anterior para que no sobreviva un elemento de otro grupo.
Definir datos y funciones
El ejemplo guarda Group A y B con sus arrays localmente, sin servidor ni plugin adicional. El valor a elige su array. Separe label visible y value enviado para que cambiar el texto no altere el identificador estable.
Cada control tiene label asociado e id único; name identifica los datos del formulario. El elemento select usa required para un valor vacío y deshabilita el hijo con disabled si no hay elementos. Los controles deshabilitados no se envían, por lo que el servidor debe comprobar valores ausentes y combinaciones incorrectas.
La opción vacía es una instrucción, no un elemento real. Asigne identificadores no vacíos a las opciones reales para distinguir una selección deliberada.
Ejemplo completo de listas relacionadas
Guarde y abra dependent-select-demo.html en UTF-8. Internet carga jQuery completo 4.0.0; no requiere servidor ni compilación. En Windows Edge con 4.0.0 y 3.7.1 se probaron teclado, reinicio, grupos vacíos/desconocidos, inspección de pares y cambios repetidos.
<!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>
Al inicio no hay selección, hay cero opciones reales aparte de la instrucción y el hijo está deshabilitado.
group=(none)
item=(none)
options=0; disabled=true
changes=0
- Group A muestra Choose an item, A-1 y A < B & C: dos elementos reales.
- A-1 e Inspect selection muestran Valid pair: {"group":"a","item":"a1"}. No se envía nada fuera.
- Group B sustituye las opciones por B-1 y B-2 y vuelve a la instrucción vacía. a1 no debe permanecer.
- Sin elegir hijo, required bloquea antes de submit. El mensaje depende de navegador e idioma.
- Group without items deshabilita el hijo con No available items. La inspección pide un par disponible.
- Reset restaura el inicio. Compruebe también selección mediante flechas del teclado.
changes cuenta ejecuciones de los manejadores change, no opciones, clics o personas. Inicio y Reset lo dejan en cero; un evento programado también cuenta.
Reconstrucción y eventos
.on("change", ...) conecta los controles existentes. El valor padre busca un array Map; una clave ausente produce array vacío. Tras vaciar y añadir instrucción y opciones, .val("") elimina la selección anterior. Los cambios A/B no acumulan opciones.
new Option(label, value) crea texto sin concatenar HTML. A < B & C se mantiene literal. Leer la selección devuelve a1 o a2, no necesariamente su texto visible.
Asignar .val() no dispara change automáticamente. Para que otra función elija Group B, ejecute esto después de inicializar y reconstruya el hijo por la misma ruta.
$("#group").val("b").trigger("change");
rebuildItem es privado dentro de ready. Use eventos o diseñe una API pública apropiada. Si reemplaza el select completo, revise los manejadores directos; puede delegar desde un padre estable.
Reset llama form.reset() y recalcula el hijo. El reinicio nativo no reconstruye opciones creadas con JavaScript. El id del botón es restore para no ocultar el método del formulario con un control llamado reset.
Validar pares y ampliar
El manejador submit evita el envío normal y comprueba pertenencia al array actual. required cubre vacíos habilitados, pero un hijo disabled de un grupo vacío queda fuera de la validación nativa. Un formulario HTML válido y un par válido para la aplicación son condiciones distintas.
FormData usa valores actuales de controles con name y omite el hijo deshabilitado. Es una demostración del navegador. Un servidor real debe comprobar de forma independiente la relación permitida usando sus datos.
Con tres niveles, cambiar el primero borra listas y valores del segundo y tercero. El tercero se llena solo tras un segundo válido. Para restaurar valores: datos padre, elección padre, generación hija y valor hijo válido.
Cargar desde servidor exige lógica adicional: limpiar valores mientras espera, distinguir carga/error/vacío y cancelar o identificar solicitudes para que una respuesta A lenta no sobrescriba B. Aquí no se implementa red ni control de concurrencia.
Si no cambia, revise ID y valores reales, orden y Console. Si queda un valor antiguo, examine la secuencia de opciones y .val(). Compruebe etiquetas, interfaz móvil nativa y textos largos sin forzar foco al siguiente campo.




