jQuery / Eltern nach der Anzahl ihrer Kinder auswählen

Filtern Sie Elternkandidaten mit .filter(function () { ... }) und vergleichen Sie darin $(this).children(".item").length. === 2 bedeutet genau zwei, >= 2 mindestens zwei. Nur für sämtliche verschachtelten Nachfahren verwenden Sie find() statt children().

Zählbereich jedes Elternteils bestimmen

.filter() behält Elemente der Ausgangsmenge, die einen Test bestehen. this ist das geprüfte Eltern-DOM-Element; true behält es. Das Ergebnis sind Eltern, nicht Kinder. Andere Eltern werden nicht aus dem Dokument entfernt.

.children(".item") zählt passende Elemente eine Ebene darunter. Ohne Selektor zählen auch Überschriften und sonstige direkte Elemente. Text- und Kommentarknoten sind in beiden Fällen ausgeschlossen.

.find(".item") erfasst tiefere Nachfahren. Beta besitzt ein direktes item mit zwei weiteren darin, also drei Nachfahren. Versteckte Elemente zählen weiterhin im DOM. Nur sichtbare Einträge brauchen eine zusätzliche Regel.

Vollständiges Beispiel für genau und mindestens

Speichern Sie alles als UTF-8 child-count-demo.html und öffnen Sie es im Browser. Internet lädt die offizielle jQuery-Datei 4.0.0. Server oder Installation sind unnötig. Bedingungen, Reset und Ergänzungen wurden in Windows Edge mit 3.7.1 und 4.0.0 ausgeführt.

Direkt zählen Alpha 2, Beta 1, Gamma 0 und Delta 3. Verstecktes D3 zählt; Überschriften und Text sind keine .item. Kandidaten sind nur direkte .group unter #board, keine inneren Objekte.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Filter parents by child count</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; }
    label { display: block; margin: 8px 0; }
    select, input, button { font: inherit; max-width: 100%; }
    .group { border: 2px solid #aaa; padding: 12px; margin: 12px 0; }
    .group.chosen { border-color: teal; background: #e5f5f1; }
    .item { display: inline-block; padding: 4px; border: 1px solid #777; margin: 3px; }
    .item[hidden] { display: none; }
    .group h2 { font-size: 20px; margin: 0; }
    #result { white-space: pre-wrap; overflow-wrap: anywhere; }
  </style>
</head>
<body>
  <label>Count scope <select id="scope"><option value="direct">Direct children</option><option value="descendant">All descendants</option></select></label>
  <label>Rule <select id="rule"><option value="exact">Exactly</option><option value="minimum">At least</option></select></label>
  <label>Required count <input id="count" type="number" min="0" step="1" value="2"></label>
  <button type="button" id="add">Add direct item to Alpha</button>
  <button type="button" id="reset">Reset</button>
  <div id="board">
    <section class="group" id="alpha"><h2>Alpha</h2><span class="item">A1</span><span class="item">A2</span> plain text <!-- comment --></section>
    <section class="group" id="beta"><h2>Beta</h2><div class="item">B1 <span class="item">B2</span><span class="item">B3</span></div></section>
    <section class="group" id="gamma"><h2>Gamma</h2>Text without an item</section>
    <section class="group" id="delta"><h2>Delta</h2><span class="item">D1</span><span class="item">D2</span><span class="item" hidden>D3 hidden</span></section>
  </div>
  <pre id="result" aria-live="polite"></pre>
  <script>
    $(function () {
      const initial = $("#board").html();
      let added = 0;
      function apply() {
        const $groups = $("#board > .group");
        const scope = $("#scope").val(), rule = $("#rule").val();
        const field = document.getElementById("count"), target = Number(field.value);
        $groups.removeClass("chosen");
        if (field.value === "" || !field.checkValidity() || !Number.isInteger(target) || target < 0) {
          $("#result").text("Enter a nonnegative integer."); return;
        }
        function count(group) {
          return scope === "direct" ? $(group).children(".item").length : $(group).find(".item").length;
        }
        const $selected = $groups.filter(function () {
          const n = count(this);
          return rule === "exact" ? n === target : n >= target;
        });
        $selected.addClass("chosen");
        const ids = $selected.map(function () { return this.id; }).get();
        const counts = $groups.map(function () { return this.id + "=" + count(this); }).get();
        $("#result").text(`scope=${scope}; rule=${rule}; count=${target}\nselected=${ids.join(",") || "(none)"}\n${counts.join("; ")}`);
      }
      $("#scope, #rule, #count").on("change input", apply);
      $("#add").on("click", function () {
        added++; $("<span>").addClass("item").text("Added " + added).appendTo("#alpha"); apply();
      });
      $("#reset").on("click", function () {
        $("#board").html(initial); added = 0;
        $("#scope").val("direct"); $("#rule").val("exact"); $("#count").val(2); apply();
      });
      apply();
    });
  </script>
</body>
</html>

Anfangs ist nur Alpha mit genau zwei direkten Einträgen markiert.

scope=direct; rule=exact; count=2
selected=alpha
alpha=2; beta=1; gamma=0; delta=3
  1. At least wählt Alpha und Delta. Genau zwei unterscheidet sich von mindestens zwei.
  2. All descendants nimmt Beta hinzu: dessen Zahl steigt von eins auf drei.
  3. Exactly 3 wählt bei Nachfahren Beta und Delta, bei Direct children nur Delta.
  4. Exactly 0 wählt Gamma; At least 0 erlaubt null und wählt alle vier.
  5. Nach Reset macht Add direct item to Alpha daraus drei; genau zwei ergibt (none). Kein Elternteil wurde gelöscht.
  6. Leer, negativ oder gebrochen zeigt einen Fehler und entfernt Markierungen. Reset stellt Struktur und Bedingungen wieder her.

Gesamtsumme und Einzelprüfung unterscheiden

Zuerst Reset, danach in den Entwicklertools ausführen. Vergleichen Sie die gemeinsame Kindmenge mit dem Elterntest:

const $groups = $("#board > .group");
console.log($groups.children(".item").length);
console.log($groups.filter(function () {
  return $(this).children(".item").length === 0;
}).map(function () { return this.id; }).get().join(","));

Sechs ist 2+1+0+3 zusammen, keine Aussage darüber, wer zwei besitzt. Die zweite Zeile prüft jede Gruppe und liefert Gamma ohne direktes .item.

6
gamma

Eine normale Funktion erhält this als aktuellen Elternteil. Ein Pfeilfunktionswechsel bei gleicher this-Verwendung bricht diese Annahme. Bei Pfeilen nutzen Sie ausdrücklich das zweite Elementargument des Rückrufs.

Eine gespeicherte Filtermenge wird nicht laufend neu bewertet. Nach Ergänzen kann die alte Menge Alpha weiter enthalten. Hier ruft Hinzufügen apply() mit aktuellen Kandidaten und Zahlen auf. .filter() beobachtet keine DOM-Änderungen.

Bereich und typische Fehler

Klären Sie, ob alle Elemente oder bestimmte Karten, li oder Steuerelemente zählen. Nur .item zählt hier; Gamma bleibt trotz h2 und Text null. Das bedeutet nicht vollständig leer. Sichtbarkeit, Verfügbarkeit oder Datenzustand brauchen bei Bedarf ausdrückliche Regeln.

Für bloßes Vorhandensein eines Nachfahren kann .has() einfacher sein. Genaue und minimale Mengen werden durch Zahlenvergleiche klar. Verwechseln Sie nth-child als Geschwisterposition nicht mit der Gesamtzahl eines Elternteils.

Bei Tausenden Eltern und tiefen wiederholten Suchen begrenzen Sie Kandidaten und unnötige Berechnung. Verwenden Sie aber keine veralteten frühen Ergebnisse nach Strukturänderung. Prüfen Sie Anfangs- und Änderungszustand.