jQuery / Choisir les éléments sans enfant particulier
Pour choisir des parents sans descendant correspondant, excluez les candidats .has(".badge") avec .not(). Pour les seuls enfants directs, testez $(this).children(".badge").length === 0 par parent. L’absence d’un enfant particulier ne signifie pas que le conteneur est entièrement vide.
Définir la cible absente et la profondeur
.has(".badge") conserve les candidats ayant un descendant correspondant à n’importe quelle profondeur. Les retirer avec .not() laisse ceux sans badge intérieur.
.children(".badge") ne vérifie qu’un niveau. Un badge imbriqué passe « aucun direct », mais pas « aucun descendant ». Masqué, il existe toujours dans le DOM, Document Object Model.
Parent signifie ici conteneur candidat, pas .parent() pour remonter. Limitez les candidats afin d’écarter ancêtres non concernés et objets intérieurs.
Exemple complet de profondeur et contenu
Enregistrez tout en UTF-8 sous missing-child-demo.html et ouvrez-le dans le navigateur. Internet charge le fichier officiel jQuery 4.0.0. Aucun serveur ou paquet n’est nécessaire. Résultats initiaux, ajout, déplacement et restauration ont été exécutés dans Edge sous Windows avec 3.7.1 et 4.0.0.
a a un badge direct, b un imbriqué, c un autre enfant et d du texte seul. e est vide, f contient un commentaire, g un espace et h un badge masqué. Les noms a: sont générés par CSS, pas des enfants DOM. Modifier les espaces internes peut modifier :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>
Sortie initiale. Le vert marque seulement le premier ensemble sans descendant badge. Les autres lignes comparent d’autres conditions.
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 n’ont aucun badge. c convient malgré son élément note.
- La deuxième ligne ajoute b : son badge est dans un wrapper, pas directement.
- Add badge to c donne d,e,f,g puis b,d,e,f,g.
- Move b badge to direct child donne aussi d,e,f,g à la deuxième ligne. Le même nœud change de relation directe, pas de présence descendante.
- Reset restaure huit conteneurs et leur structure. Le badge masqué de h reste présent.
Exclusion du candidat et vide
Après Reset, exécutez dans les outils de développement. La première expression teste les candidats eux-mêmes, la seconde leur contenu :
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(","));
Aucune des huit cards n’est elle-même .badge : le premier résultat comprend tout. Le second donne le résultat voulu c,d,e,f,g.
a,b,c,d,e,f,g,h
c,d,e,f,g
:empty signifie sans enfant élément ou texte, pas sans une espèce particulière. e et f avec commentaire conviennent. Un commentaire ne bloque pas, l’espace texte de g oui ; le texte de d aussi.
.children().length === 0 signifie sans enfant élément direct, soit d,e,f,g. Texte et espaces ne comptent pas. Les noms CSS ::before ne sont pas des enfants DOM et ne changent pas e/f. Apparence et structure sont distinctes.
Changements et application
Un résultat enregistré ne se met pas à jour après ajout d’un badge. Ajout et déplacement rappellent report() avec les candidats actuels. Reliez aussi la réévaluation aux changements réalisés ailleurs.
.not() et .filter() construisent des sélections sans supprimer d’autres cards. Classes ou messages d’état vide sont des actions séparées. L’exemple efface les anciennes marques avant d’appliquer les nouvelles.
Définissez balise ou classe, profondeur directe ou complète, et traitement du masqué ou désactivé. Comparez structure initiale et modifiée au lieu d’échanger aveuglément des sélecteurs ressemblants. Limitez les candidats des recherches profondes répétées.




