Skip to content

Alles für WordPress, Webentwicklung — und mehr

🔍 JQuery: so finden sie ein element mit bestimmtem text

🔍 JQuery: so finden sie ein element mit bestimmtem text

Ein Entwickler erstellt ein Produktkarten-Layout in WooCommerce. Alles läuft nach Plan, bis aus dem Admin-Panel die Aufgabe kommt: „Entfernen Sie das Featured-Badge und das Komma danach, aber nur für Produkte innerhalb der Holder-Kategorie." Eine manuelle Bearbeitung kommt nicht infrage, es handelt sich um Hunderte von Produkten. Wir benötigen ein Skript, das den Block mit dem Text „featured" eigenständig findet, zum Elternelement aufsteigt, das benachbarte span-Element greift und die gesamte Konstruktion entfernt.

Und hier kommt jQuery mit seinen Verkettungen ins Spiel: .each() für die Iteration, .find() für die Suche, :contains() für die Filterung nach Text, .closest() für den Aufstieg zum Elternelement, .next() für den Sprung zum Nachbarelement und .remove() für die Bereinigung. Fünf Methoden, und die Aufgabe ist in vier Zeilen gelöst. Im Folgenden analysiere ich jeden Schritt: vom Selektor bis zur endgültigen Bereinigung, und erläutere die Logik und die Fallstricke.

💡 Kurzüberblick:

  • Zuerst finden wir den Zielblock mit Text über den Contains-Selektor und steigen über closest zu seinem Elternelement auf
  • Dann springen wir über next zum benachbarten Element und entfernen beide Blöcke präzise über remove
  • Am Ende debuggen wir das Skript und analysieren typische Fehler mit Kontextverlust und gierigem next

Aufgabe: Wonach wir suchen und was wir entfernen

Die ursprüngliche HTML-Struktur sieht ungefähr so aus:

1<span class="tg-cats-holder">
2 <a class="product_visibility" href="...">
3 <span class="tg-item-term">featured</span>
4 </a>
5 <span>, </span>
6 <a class="product_visibility" href="...">
7 <span class="tg-item-term">sale</span>
8 </a>
9 <span>, </span>
10</span>

Das Hauptziel ist, <span class="tg-item-term"> zu finden, das den Text featured enthält, zum Elternelement <a class="product_visibility"> aufzusteigen, dann zum nächsten <span> (mit dem Komma) zu springen und es zu entfernen. Danach das <a class="product_visibility"> mit dem Badge selbst entfernen. Andere Labels (sale usw.) bleiben unberührt.

Fertiges Skript

1jQuery(document).ready(function($) {
2 $('span.tg-cats-holder').each(function() {
3 $(this)
4 .find('span.tg-item-term:contains("featured")')
5 .closest('a.product_visibility')
6 .next('span:contains(",")')
7 .remove();
8
9 $(this)
10 .find('span.tg-item-term:contains("featured")')
11 .closest('a.product_visibility')
12 .remove();
13 });
14});

Der Code ist bewusst in zwei Ketten aufgeteilt und nicht in eine zusammengefasst. Der Grund ist einfach: Nach .remove() eines Elements bricht die Kette ab, es gibt keinen Weg mehr weiter. Also bereinigen wir zuerst das benachbarte span mit dem Komma (erste Kette) und entfernen dann den Block selbst (zweite).

Schritt-für-Schritt-Analyse

Schritt 1: Jeden Holder betreten

1$('span.tg-cats-holder').each(function() {

Die Methode .each() durchläuft jedes Element in der Sammlung, in unserem Fall alle <span class="tg-cats-holder"> auf der Seite. Gibt es mehrere Holder (verschiedene Produktkategorien), arbeitet das Skript in jedem unabhängig. Innerhalb der Callback-Funktion zeigt this auf den aktuellen Holder, alle weiteren Suchvorgänge gehen von ihm aus.

Schritt 2: Suche nach dem Block mit Text über:contains()

1.find('span.tg-item-term:contains("featured")')

Der Selektor :contains("featured") findet ein Element, in dem (einschließlich der Kindknoten) die Zeichenkette „featured" vorkommt. Drei wichtige Nuancen:

  • Groß-/Kleinschreibung. Die Zeichenketten „Featured", „FEATURED" und „featured" sind unterschiedlich. Wird das Badge im Admin-Panel anders geschrieben, fügen Sie eine Prüfung über .filter() mit .toLowerCase() hinzu, Beispiel im FAQ-Abschnitt.

  • Suche durch alle Nachkommen. :contains() prüft den Textinhalt sowohl des Elements selbst als auch aller seiner Kinder. Befindet sich ein weiteres <strong> innerhalb von <span class="tg-item-term">, zählt der darin enthaltene Text ebenfalls.

  • Dies ist kein Performance-Selektor. :contains() wird nicht durch natives querySelectorAll beschleunigt, jQuery geht alle Kandidaten durch und liest .textContent. Bei Seiten mit Hunderten von Elementen ist es besser, die Auswahl durch eine zusätzliche Klasse oder ein Attribut einzugrenzen.

Die Methode .find() beschränkt die Suche auf die Nachkommen des aktuellen Holders, nicht auf die gesamte Seite, das sorgt sowohl für einen Geschwindigkeitsschub als auch für Schutz vor Falschtreffern.

Schritt 3: Aufstieg zum Elternelement über.closest()

1.closest('a.product_visibility')

.closest() geht den Baum vom gefundenen Element aus nach oben und gibt den nächsten Vorfahren zurück, der auf den Selektor passt. Anders als .parent() ist es nicht auf eine Ebene beschränkt, es steigt bis zu <html> auf, bis es eine Übereinstimmung findet oder die Wurzel erreicht.

Hätten wir .parent() anstelle von .closest() genommen, würde das Skript bei jeder Verschachtelung brechen: <span class="tg-item-term"><strong><a class="product_visibility">, .parent() würde <strong> zurückgeben, nicht den benötigten Link. .closest() sichert gegen solche Überraschungen ab.

Schritt 4: Sprung zum Nachbarelement über.next()

1.next('span:contains(",")')

.next() nimmt das unmittelbar folgende Geschwisterelement desselben Elternelements. Der Filter span:contains(",") garantiert, dass wir genau das span mit dem Komma nehmen, nicht irgendein folgendes Element. Ohne den Filter würde .next() den ersten verfügbaren Geschwisterknoten greifen, zum Beispiel ein weiteres <a class="product_visibility"> mit dem nächsten Label.

Der Unterschied zwischen .next() und .nextAll(): Das erste nimmt genau ein Element, das zweite alle nachfolgenden Geschwister. Hier brauchen wir einen präzisen Schlag, also .next().

Schritt 5: Entfernen

1.remove();

.remove() schneidet das Element vollständig aus dem DOM, samt Event-Handlern und jQuery-Daten. Wenn Sie nur ausblenden möchten (mit der Aussicht auf Rückkehr), verwenden Sie .hide() oder .detach(). Aber für die Aufgabe „Badge für immer entfernen" nur .remove().

Die Reihenfolge ist wichtig: Zuerst entfernen wir das benachbarte span (erste Kette), erst dann den Block selbst (zweite). Entfernen wir den Block zuerst, verliert das benachbarte span den Kontext, .next() arbeitet von einem anderen Element aus oder gibt eine leere Sammlung zurück.

Erweitertes Beispiel: Suche ohne Beachtung der Groß-/Kleinschreibung

In einem realen Projekt kann der Label-Text in unterschiedlicher Schreibweise vorkommen: featured, Featured, FEATURED. Das standardmäßige :contains() bewältigt das nicht. Die Lösung ist ein benutzerdefinierter Selektor auf Basis von .filter():

1$('span.tg-cats-holder').each(function() {
2 $(this).find('span.tg-item-term').filter(function() {
3 return $(this).text().toLowerCase().indexOf('featured') !== -1;
4 }).closest('a.product_visibility').each(function() {
5 $(this).next('span:contains(",")').remove();
6 $(this).remove();
7 });
8});

Hier prüft .filter() mit einer Callback-Funktion den kleingeschriebenen Text jedes Kandidaten. Es sind etwas mehr Zeilen, aber das Skript hängt nicht davon ab, wie genau der Text im Admin-Panel eingegeben wurde.

Alternative in reinem JavaScript

Wird jQuery im Projekt überhaupt nicht verwendet, schreibt man dieselbe Logik in Vanilla JS:

1document.querySelectorAll('span.tg-cats-holder').forEach(function(holder) {
2 holder.querySelectorAll('span.tg-item-term').forEach(function(term) {
3 if (term.textContent.includes('featured')) {
4 const link = term.closest('a.product_visibility');
5 const nextSpan = link.nextElementSibling;
6 if (nextSpan && nextSpan.matches('span') && nextSpan.textContent.includes(',')) {
7 nextSpan.remove();
8 }
9 link.remove();
10 }
11 });
12});

Derselbe Algorithmus: Holder durchlaufen → Suche nach Text über .textContent.includes() → Aufstieg zum Elternelement über natives .closest() → Nachbarelement prüfen und entfernen → Block entfernen. Die native Implementierung ist länger, zieht aber keine jQuery-Abhängigkeit nach sich.

Kurzes Video zum Thema, Analyse von :contains() und Navigationsketten in jQuery:

⁉️🤔 Häufig gestellte Fragen

Warum findet :contains() Text nicht, den ich auf der Seite sehe?

Höchstwahrscheinlich liegt das Problem an der Groß-/Kleinschreibung oder an Leerzeichen. :contains("Featured") findet „featured" nicht, dies ist ein Selektor mit Beachtung der Groß-/Kleinschreibung. Zudem kann der Browser Leerzeichen innerhalb von HTML anders normalisieren, als Sie erwarten. Prüfen Sie den exakten Inhalt über console.log($('selector').text()) und kopieren Sie die Zeichenkette eins zu eins in :contains().

Kann man auf .closest() verzichten und eine feste Kette .parent().parent() verwenden?

Technisch ja. Praktisch nicht zu empfehlen. Eine feste Kette .parent().parent() bricht bei jeder Änderung der Verschachtelung: Ein Wrapper wurde hinzugefügt, ein Theme-Wechsel, das Skript steht still. .closest('a.product_visibility') ist flexibler: Es sucht das nächste passende Element auf jeder beliebigen Ebene nach oben. Einmal geschrieben, funktioniert es unabhängig von der Struktur.

Das Skript entfernt das falsche span. Wie debuggt man das?

Erster Schritt: Fügen Sie console.log() vor jedem .remove() ein und prüfen Sie, was genau in die Auswahl gelangt. Zweitens: Verfeinern Sie den Selektor für .next(). Ohne Filter nimmt .next() jedes folgende Element auf derselben Ebene, auch wenn es ein weiteres <a> oder <div> ist. Fügen Sie span:contains(",") oder eine spezifischere Klasse hinzu und verifizieren Sie, dass der Nachbar tatsächlich der richtige ist.

Muss das Skript in jQuery(document).ready() eingewickelt werden?

Ja, wenn das Skript im <head> steht oder vor dem DOM-Rendering eingebunden wird. Die Konstruktion jQuery(document).ready(function($){ ... }) garantiert, dass der Code erst nach dem vollständigen Laden des DOM-Baums ausgeführt wird. Zudem wird $ explizit in den Callback übergeben, das sichert gegen Konflikte mit anderen Bibliotheken ab, die ebenfalls Anspruch auf $ erheben könnten. Steht das Skript im Footer nach allen Elementen, ist .ready() formal nicht erforderlich, aber die Disziplin des „immer Einwickelns" bewahrt vor schwer auffindbaren Fehlern beim Refactoring.

Was ist schneller: jQuery oder Vanilla JS für eine solche Aufgabe?

Natives JavaScript gewinnt beim Kaltstart: Keine Notwendigkeit, jQuery zu laden, zu parsen und zu kompilieren, und das spart Dutzende Kilobyte Traffic und einige Millisekunden Parsing-Zeit. Bei DOM-Operationen ist der Abstand bescheidener, es geht um Bruchteile einer Millisekunde pro Aufruf. Für eine typische WordPress-Site, auf der jQuery bereits durch das Theme oder Plugins eingebunden ist, ist der Unterschied nicht wahrnehmbar, Ihr vierzeiliges Skript wird nicht zum Flaschenhals. Ist jQuery bereits im Projekt, schreiben Sie in jQuery, der Code ist kompakter und lesbarer. Wenn nicht, nehmen Sie Vanilla JS, ziehen Sie keine Bibliothek für einen einzigen Handler herein.

Lohnt es sich, jQuery im Jahr 2026 für die Textsuche zu verwenden?

jQuery ist nicht verschwunden. Laut W3Techs-Statistik für Juni 2026 ist die Bibliothek auf 87,3% der Websites mit einer bekannten JavaScript-Bibliothek installiert (68,1% aller Websites), hauptsächlich dank WordPress, wo jQuery im Kern steckt und automatisch von Themes eingebunden wird. Für einen WordPress-Entwickler ist jQuery ein natives Werkzeug, das bereits geladen ist und keine zusätzlichen Requests erzeugt.

Direkte Alternativen wie Alpine.js oder HTMX lösen andere Aufgaben (Reaktivität, partielle Updates) und sind für ein einmaliges DOM-Skript überdimensioniert. Natives querySelectorAll + closest() + remove() ist eine funktionierende Option ohne Abhängigkeiten, aber die Aufrufketten werden länger. Die Wahl läuft auf eine einfache Regel hinaus: jQuery ist bereits im Projekt, nehmen Sie jQuery; Projekt ohne jQuery, nehmen Sie Vanilla JS.

Die Hauptsache ist, dass Sie jetzt die exakte Mechanik der Kombination :contains() + .closest() + .next() + .remove() kennen, und in welchem Dialekt man sie schreibt, entscheidet das konkrete Projekt.