
🔍 JQuery: jak znaleźć element z określonym tekstem
Programista tworzy kartę produktu w WooCommerce. Wszystko idzie zgodnie z planem, dopóki z panelu administracyjnego nie przychodzi zadanie: „usunąć plakietkę Featured i przecinek po niej, ale tylko dla produktów wewnątrz kategorii-holdera". Ręczne przechodzenie nie wchodzi w grę, produktów są setki. Potrzebny jest skrypt, który sam znajdzie blok z tekstem „featured", dotrze do elementu nadrzędnego, przechwyci sąsiedni span i usunie całą konstrukcję.
I tutaj na scenę wkracza jQuery ze swoimi łańcuchami: .each() do iteracji, .find() do wyszukiwania, :contains() do filtrowania po tekście, .closest() do przejścia w górę do elementu nadrzędnego, .next() do przejścia do sąsiada i .remove() do sprzątania. Pięć metod i zadanie rozwiązane w czterech linijkach. Poniżej rozkładam każdy krok: od selektora do finalnego czyszczenia, z wyjaśnieniem logiki i pułapek.
💡 Szybki przegląd:
- Najpierw znajdujemy docelowy blok z tekstem za pomocą selektora contains i przechodzimy w górę do jego elementu nadrzędnego przez closest
- Następnie przechodzimy do sąsiedniego elementu przez next i punktowo usuwamy oba bloki przez remove
- Na koniec dostrajamy skrypt i omawiamy typowe błędy z utratą kontekstu oraz zachłannym next
Zadanie: czego szukamy i co usuwamy
Wygląd źródłowej struktury HTML jest mniej więcej taki:
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>
Kluczowy cel: znaleźć <span class="tg-item-term">, wewnątrz którego znajduje się tekst featured, przejść w górę do nadrzędnego <a class="product_visibility">, następnie przejść do następnego po nim <span> (z przecinkiem) i usunąć go. Potem usunąć sam <a class="product_visibility"> z plakietką. Innych etykiet (sale itp.) nie ruszamy.
Gotowy skrypt
1 jQuery(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 });
Kod celowo został rozbity na dwa łańcuchy, a nie zwinięty w jeden. Powód jest prosty: po .remove() elementu łańcuch się urywa, nie ma do czego dalej przechodzić. Dlatego najpierw czyścimy sąsiedni span z przecinkiem (pierwszy łańcuch), potem usuwamy sam blok (drugi).
Analiza krok po kroku
Krok 1: wchodzimy do każdego holdera
1 $('span.tg-cats-holder').each(function() {
Metoda .each() przechodzi przez każdy element kolekcji, w naszym przypadku przez wszystkie <span class="tg-cats-holder"> na stronie. Jeśli holderów jest kilka (różne kategorie produktów), skrypt wykona się w każdym niezależnie. Wewnątrz funkcji callback this wskazuje na bieżący holder, całe dalsze wyszukiwanie odbywa się od niego.
Krok 2: szukamy bloku z tekstem przez:contains()
1 .find('span.tg-item-term:contains("featured")')
Selektor :contains("featured") znajduje element, wewnątrz którego (włącznie z węzłami potomnymi) znajduje się podciąg „featured". Trzy ważne niuanse:
Wrażliwość na wielkość liter. Ciągi „Featured", „FEATURED" i „featured" są różne. Jeśli w panelu administracyjnym plakietka jest zapisywana różnie, należy dodać sprawdzenie przez
.filter()z.toLowerCase(), przykład w sekcji FAQ.Wyszukiwanie we wszystkich potomkach.
:contains()sprawdza zawartość tekstową zarówno samego elementu, jak i wszystkich jego dzieci. Jeśli wewnątrz<span class="tg-item-term">znajduje się kolejny<strong>, tekst wewnątrz niego również jest uwzględniany.To nie jest selektor wydajności.
:contains()nie jest przyspieszany przez natywnyquerySelectorAll, jQuery przechodzi przez wszystkich kandydatów i odczytuje.textContent. W przypadku stron z setkami elementów lepiej zawęzić próbkę dodatkową klasą lub atrybutem.
Metoda .find() ogranicza wyszukiwanie do potomków bieżącego holdera, a nie całej strony, co daje zarówno wzrost szybkości, jak i ochronę przed fałszywymi trafieniami.
Krok 3: przechodzimy w górę do elementu nadrzędnego przez.closest()
1 .closest('a.product_visibility')
.closest() idzie w górę drzewa od znalezionego elementu i zwraca najbliższego przodka, który pasuje do selektora. W przeciwieństwie do .parent(), nie jest ograniczony do jednego poziomu, będzie szedł w górę aż do <html>, dopóki nie znajdzie dopasowania lub nie natrafi na korzeń.
Gdyby zamiast .closest() użyć .parent(), skrypt zepsułby się przy dowolnym zagnieżdżeniu: <span class="tg-item-term"> → <strong> → <a class="product_visibility">, .parent() zwróciłby <strong>, a nie potrzebny link. .closest() zabezpiecza przed takimi niespodziankami.
Krok 4: przeskakujemy do sąsiada przez.next()
1 .next('span:contains(",")')
.next() bierze następny bezpośrednio sąsiedni element tego samego elementu nadrzędnego. Filtr span:contains(",") gwarantuje, że pobierzemy dokładnie span z przecinkiem, a nie jakikolwiek następny element. Bez filtra .next() chwyciłby pierwszy lepszy sąsiedni węzeł, na przykład inny <a class="product_visibility"> z następną etykietą.
Różnica między .next() a .nextAll(): pierwszy bierze dokładnie jeden element, drugi wszystkich następnych sąsiadów. Tutaj potrzebne jest punktowe uderzenie, dlatego .next().
Krok 5: usuwamy
1 .remove();
.remove() wycina element z DOM całkowicie, razem z procedurami obsługi zdarzeń i danymi jQuery. Jeśli trzeba tylko ukryć (z perspektywą przywrócenia), należy użyć .hide() lub .detach(). Ale do zadania „usunąć plakietkę na zawsze" tylko .remove().
Ważna kolejność: najpierw usuwamy sąsiedni span (pierwszy łańcuch), dopiero potem sam blok (drugi). Jeśli usuniemy blok jako pierwszy, sąsiedni span straci kontekst, .next() wykona się od innego elementu lub zwróci pustą kolekcję.
Rozszerzony przykład: wyszukiwanie niewrażliwe na wielkość liter
W rzeczywistym projekcie tekst etykiety może występować z różną wielkością liter: featured, Featured, FEATURED. Standardowy :contains() tutaj nie poradzi. Rozwiązanie: niestandardowy selektor na bazie .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 });
Tutaj .filter() z funkcją callback sprawdza tekst każdego kandydata sprowadzony do małych liter. Linijek jest nieco więcej, ale skrypt nie zależy od tego, jak dokładnie wprowadzono tekst w panelu administracyjnym.
Alternatywa w czystym JavaScripcie
Jeśli jQuery w ogóle nie jest używane w projekcie, tę samą logikę zapisuje się w vanilla JS:
1 document.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 });
Ten sam algorytm: iteracja holderów → wyszukiwanie po tekście przez .textContent.includes() → przejście w górę do elementu nadrzędnego przez natywny .closest() → sprawdzenie i usunięcie sąsiada → usunięcie bloku. Natywna implementacja jest dłuższa, ale nie ciągnie za sobą zależności od jQuery.
Krótkie wideo na temat, omówienie :contains() i łańcuchów nawigacji w jQuery:
⁉️🤔 Często zadawane pytania
Dlaczego :contains() nie znajduje tekstu, który widzę na stronie?
Najprawdopodobniej problem leży w wielkości liter lub spacjach.
:contains("Featured")nie znajdzie „featured", to selektor wrażliwy na wielkość liter. Dodatkowo przeglądarka może normalizować spacje wewnątrz HTML nie tak, jak Pan/Pani oczekuje. Proszę sprawdzić dokładną zawartość przezconsole.log($('селектор').text())i skopiować ciąg jeden do jednego do:contains().
Czy można obejść się bez .closest() i użyć stałego łańcucha .parent().parent()?
Technicznie tak. Praktycznie nie warto. Sztywny łańcuch
.parent().parent()zepsuje się przy każdej zmianie zagnieżdżenia: dodano opakowanie, zmieniono na inny motyw, skrypt przestał działać..closest('a.product_visibility')jest bardziej elastyczny: szuka najbliższego pasującego elementu na dowolnym poziomie w górę. Napisane raz, działa niezależnie od struktury.
Skrypt usuwa nie ten span. Jak to debugować?
Pierwszy krok: wstawić
console.log()przed każdym.remove()i sprawdzić, co dokładnie trafia do próbki. Drugi: uściślić selektor dla.next(). Bez filtra.next()bierze dowolny następny element tego samego poziomu, nawet jeśli jest to inny<a>lub<div>. Proszę dodaćspan:contains(",")lub bardziej konkretną klasę i sprawdzić, czy sąsiad jest rzeczywiście tym właściwym.
Czy trzeba opakowywać skrypt w jQuery(document).ready()?
Tak, jeśli skrypt jest w
<head>lub podłączany przed renderowaniem DOM. KonstrukcjajQuery(document).ready(function($){ ... })gwarantuje, że kod wykona się dopiero po pełnym załadowaniu drzewa DOM. Dodatkowo$jest przekazywane jawnie do callback, co zabezpiecza przed konfliktami z innymi bibliotekami, które również mogą rościć sobie prawo do$. Jeśli skrypt jest w stopce po wszystkich elementach,.ready()formalnie nie jest obowiązkowe, ale dyscyplina „zawsze opakowywać" ratuje przed trudnymi do wyłapania błędami podczas refaktoryzacji.
Co jest szybsze: jQuery czy vanilla JS do takiego zadania?
Natywny JavaScript wygrywa na zimnym starcie: nie trzeba ładować, parsować i kompilować jQuery, a to oszczędność dziesiątek kilobajtów transferu i kilku milisekund parsowania. Na operacjach DOM różnica jest skromniejsza, chodzi o ułamki milisekundy na jedno wywołanie. Dla typowej strony na WordPressie, gdzie jQuery jest już podłączone przez motyw lub wtyczki, różnica jest niezauważalna, Pana/Pani czterolinijkowy skrypt nie stanie się wąskim gardłem. Jeśli jQuery jest już w projekcie, proszę pisać w jQuery, kod jest bardziej zwięzły i czytelny. Jeśli go nie ma, proszę wziąć vanilla JS, nie ciągnąć biblioteki dla jednej procedury obsługi.
Czy warto używać jQuery do wyszukiwania po tekście w 2026 roku?
jQuery nigdzie nie zniknęło. Według statystyk W3Techs na czerwiec 2026 roku, biblioteka jest zainstalowana na 87,3% stron ze znaną biblioteką JavaScript (68,1% wszystkich stron), głównie dzięki WordPress, gdzie jQuery jest w jądrze i automatycznie podłączane przez motywy. Dla developera WordPressa jQuery to natywne narzędzie, które jest już załadowane i nie generuje dodatkowych zapytań.
Bezpośrednie alternatywy, takie jak Alpine.js czy HTMX, rozwiązują inne zadania (reaktywność, partial updates) i dla jednorazowego skryptu DOM są nadmiarowe. Natywny querySelectorAll + closest() + remove() to działająca opcja bez zależności, ale łańcuchy wywołań są dłuższe. Wybór sprowadza się do prostej zasady: jQuery jest już w projekcie, proszę brać jQuery; projekt bez jQuery, proszę brać vanilla JS.
Najważniejsze, że zna Pan/Pani teraz dokładną mechanikę połączenia :contains() + .closest() + .next() + .remove(), a w jakim dialekcie to zapisać, decyduje konkretny projekt.



