jQuery / Elegir elementos sin un hijo específico
Para elegir padres sin un descendiente coincidente, excluya candidatos .has(".badge") con .not(). Para hijos directos, pruebe $(this).children(".badge").length === 0 por padre. No tener un hijo específico no significa estar completamente vacío.
Definir objetivo ausente y profundidad
.has(".badge") conserva candidatos con descendiente coincidente a cualquier profundidad. Quitarlos con .not() deja los que no tienen badge dentro.
.children(".badge") comprueba un nivel. Un badge anidado cumple «sin directo», pero no «sin descendiente». Oculto sigue existiendo en el DOM, Document Object Model.
Padre significa contenedor candidato, no .parent() para subir. Limite candidatos para no incluir antecesores ajenos u objetos interiores.
Ejemplo completo de profundidad y contenido
Guarde todo en UTF-8 como missing-child-demo.html y ábralo en el navegador. Internet carga el archivo oficial de jQuery 4.0.0. No requiere servidor ni paquetes. Inicio, adición, movimiento y reinicio se ejecutaron en Edge para Windows con 3.7.1 y 4.0.0.
a tiene badge directo, b anidado, c otro hijo y d solo texto. e está vacío, f tiene comentario, g un espacio y h badge oculto. Los nombres a: son CSS, no hijos DOM. Cambiar espacios internos puede cambiar :empty.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Select containers without a badge</title>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<style>
body { font: 16px/1.6 sans-serif; margin: 20px; max-width: 760px; }
button { font: inherit; margin: 4px; }
.card { border: 2px solid #aaa; margin: 10px 0; padding: 10px; min-height: 28px; }
.card::before { content: attr(id) ": "; font-weight: bold; }
.card.chosen { border-color: teal; background: #e4f6f0; }
.badge { border: 1px solid #777; padding: 2px; }
.badge[hidden] { display: none; }
#result { white-space: pre-wrap; overflow-wrap: anywhere; }
</style>
</head>
<body>
<p>Green cards have no .badge descendant. Names are CSS-generated labels.</p>
<button type="button" id="add">Add badge to c</button>
<button type="button" id="move">Move b badge to direct child</button>
<button type="button" id="reset">Reset</button>
<div id="cards">
<div class="card" id="a">Direct <span class="badge">A</span></div>
<div class="card" id="b"><div class="wrapper">Nested <span class="badge">B</span></div></div>
<div class="card" id="c"><span class="note">Other element</span></div>
<div class="card" id="d">Text only</div>
<div class="card" id="e"></div>
<div class="card" id="f"><!-- comment only --></div>
<div class="card" id="g"> </div>
<div class="card" id="h"><span class="badge" hidden>Hidden</span></div>
</div>
<pre id="result" aria-live="polite"></pre>
<script>
$(function () {
const initial = $("#cards").html();
function ids($set) { return $set.map(function () { return this.id; }).get().join(",") || "(none)"; }
function report() {
const $cards = $("#cards > .card");
const $withoutDescendant = $cards.not($cards.has(".badge"));
const $withoutDirect = $cards.filter(function () { return $(this).children(".badge").length === 0; });
const $empty = $cards.filter(":empty");
const $withoutElements = $cards.filter(function () { return $(this).children().length === 0; });
$cards.removeClass("chosen"); $withoutDescendant.addClass("chosen");
$("#result").text([
"no descendant badge=" + ids($withoutDescendant),
"no direct badge=" + ids($withoutDirect),
"empty=" + ids($empty),
"no element children=" + ids($withoutElements)
].join("\n"));
}
$("#add").on("click", function () {
$("<span>").addClass("badge").text("New").appendTo("#c");
$(this).prop("disabled", true); report();
});
$("#move").on("click", function () {
$("#b .badge").appendTo("#b"); $(this).prop("disabled", true); report();
});
$("#reset").on("click", function () {
$("#cards").html(initial); $("#add, #move").prop("disabled", false); report();
});
report();
});
</script>
</body>
</html>
Salida inicial. Verde destaca solo el primer conjunto sin descendiente badge; las otras líneas comparan condiciones distintas.
no descendant badge=c,d,e,f,g
no direct badge=b,c,d,e,f,g
empty=e,f
no element children=d,e,f,g
- c,d,e,f,g no tienen badge. c cumple pese a su elemento note.
- La segunda línea añade b: su badge está dentro de un wrapper.
- Add badge to c da d,e,f,g y luego b,d,e,f,g.
- Move b badge to direct child da también d,e,f,g en la segunda. El mismo nodo cambia relación directa, no presencia descendiente.
- Reset restaura ocho contenedores y estructura. El badge oculto de h sigue presente.
Exclusión del candidato y vacío
Tras Reset, ejecute en herramientas de desarrollo. La primera expresión examina candidatos mismos y la segunda su interior:
const $cards = $("#cards > .card");
console.log($cards.not(".badge").map(function () { return this.id; }).get().join(","));
console.log($cards.filter(function () {
return $(this).find(".badge").length === 0;
}).map(function () { return this.id; }).get().join(","));
Ninguna de las ocho cards es .badge, así que la primera devuelve todas. La segunda da el resultado buscado c,d,e,f,g.
a,b,c,d,e,f,g,h
c,d,e,f,g
:empty significa sin hijos elemento o texto, no sin un tipo específico. e y f con comentario cumplen. Un comentario no impide coincidir; el espacio de g sí, igual que el texto de d.
.children().length === 0 significa sin hijos elemento directos: d,e,f,g. Texto y espacios no cuentan. Nombres CSS ::before tampoco son hijos DOM y no cambian e/f. Apariencia y estructura son distintas.
Cambios y aplicación práctica
Un resultado guardado no se actualiza tras añadir badge. Adición y movimiento llaman report() con candidatos actuales. Conecte también la reevaluación con cambios hechos por otro código.
.not() y .filter() crean selecciones sin eliminar otras cards. Clases o mensajes de estado vacío son acciones aparte. Se quitan resaltados anteriores antes de poner los actuales.
Defina etiqueta o clase, profundidad directa o completa y tratamiento de ocultos o deshabilitados. Compare estructura inicial y modificada sin intercambiar selectores parecidos a ciegas. Limite candidatos en búsquedas profundas repetidas.




