
🔍 JQuery: come trovare un elemento con un testo specifico
Uno sviluppatore sta creando il layout di una scheda prodotto in WooCommerce. Tutto procede come previsto finché dal pannello di amministrazione arriva un task: «rimuovi il badge Featured e la virgola dopo di esso, ma solo per i prodotti all'interno della categoria holder». L'elaborazione manuale non è un'opzione, ci sono centinaia di prodotti. Serve uno script che trovi il blocco con il testo "featured" in autonomia, risalga al genitore, prenda lo span adiacente e rimuova l'intera costruzione.
Ed è qui che entra in scena jQuery con le sue catene: .each() per l'iterazione, .find() per la ricerca, :contains() per filtrare in base al testo, .closest() per risalire al genitore, .next() per spostarsi all'elemento adiacente e .remove() per la pulizia. Cinque metodi e il task è risolto in quattro righe. Di seguito analizzo ogni passaggio: dal selettore alla pulizia finale, spiegando la logica e le insidie.
💡 Panoramica rapida:
- Prima troviamo il blocco target con il testo tramite il selettore contains e risaliamo al suo genitore tramite closest
- Poi ci spostiamo all'elemento adiacente tramite next e rimuoviamo con precisione entrambi i blocchi tramite remove
- Alla fine facciamo il debug dello script e analizziamo gli errori tipici di perdita di contesto e next avido
Task: cosa cerchiamo e cosa rimuoviamo
La struttura HTML originale ha più o meno questo aspetto:
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>
L'obiettivo chiave è trovare <span class="tg-item-term"> che contiene il testo featured, risalire al genitore <a class="product_visibility">, poi spostarsi allo <span> successivo (con la virgola) e rimuoverlo. Dopodiché, rimuovere <a class="product_visibility"> con il badge stesso. Le altre etichette (sale, ecc.) restano intatte.
Script pronto
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 });
Il codice è volutamente diviso in due catene anziché essere compresso in una sola. Il motivo è semplice: dopo .remove() di un elemento, la catena si interrompe, non c'è modo di proseguire oltre. Quindi prima puliamo lo span adiacente con la virgola (prima catena), poi rimuoviamo il blocco stesso (seconda).
Analisi passo passo
Passo 1: entrare in ogni holder
1 $('span.tg-cats-holder').each(function() {
Il metodo .each() scorre ogni elemento nella collezione, nel nostro caso tutti gli <span class="tg-cats-holder"> sulla pagina. Se ci sono più holder (categorie di prodotto diverse), lo script lavorerà in ciascuno in modo indipendente. All'interno della funzione di callback, this punta all'holder corrente, tutte le ricerche successive partono da lì.
Passo 2: cercare il blocco con il testo tramite:contains()
1 .find('span.tg-item-term:contains("featured")')
Il selettore :contains("featured") trova un elemento al cui interno (inclusi i nodi figli) è presente la sottostringa "featured". Tre sfumature importanti:
Sensibilità alle maiuscole. Le stringhe "Featured", "FEATURED" e "featured" sono diverse. Se il badge è scritto diversamente nel pannello di amministrazione, aggiungi un controllo tramite
.filter()con.toLowerCase(), esempio nella sezione FAQ.Ricerca in tutti i discendenti.
:contains()controlla il contenuto testuale sia dell'elemento stesso sia di tutti i suoi figli. Se dentro<span class="tg-item-term">c'è un altro<strong>, anche il testo al suo interno conta.Non è un selettore di performance.
:contains()non è accelerato dal nativoquerySelectorAll, jQuery scorre tutti i candidati e legge.textContent. Per pagine con centinaia di elementi, è meglio restringere la selezione con una classe o un attributo aggiuntivo.
Il metodo .find() limita la ricerca ai discendenti dell'holder corrente, non all'intera pagina, questo fornisce sia un aumento di velocità sia una protezione dai falsi positivi.
Passo 3: risalire al genitore tramite.closest()
1 .closest('a.product_visibility')
.closest() risale l'albero dall'elemento trovato e restituisce l'antenato più vicino che corrisponde al selettore. A differenza di .parent(), non è limitato a un solo livello, risalirà fino a <html> finché non trova una corrispondenza o raggiunge la radice.
Se avessimo usato .parent() invece di .closest(), lo script si romperebbe con qualsiasi annidamento: <span class="tg-item-term"> → <strong> → <a class="product_visibility">, .parent() restituirebbe <strong>, non il link necessario. .closest() mette al riparo da queste sorprese.
Passo 4: saltare all'elemento adiacente tramite.next()
1 .next('span:contains(",")')
.next() prende l'elemento fratello immediatamente successivo con lo stesso genitore. Il filtro span:contains(",") garantisce che prendiamo esattamente lo span con la virgola, non un qualsiasi elemento successivo. Senza il filtro, .next() prenderebbe il primo nodo fratello disponibile, per esempio un altro <a class="product_visibility"> con l'etichetta successiva.
La differenza tra .next() e .nextAll(): il primo prende esattamente un elemento, il secondo prende tutti i fratelli successivi. Qui serve un colpo preciso, quindi .next().
Passo 5: rimuovere
1 .remove();
.remove() taglia l'elemento dal DOM completamente, insieme ai gestori di eventi e ai dati jQuery. Se devi solo nascondere (con la prospettiva di ripristinare), usa .hide() o .detach(). Ma per il task «rimuovere il badge per sempre», solo .remove().
L'ordine conta: prima rimuoviamo lo span adiacente (prima catena), solo dopo il blocco stesso (seconda). Se rimuoviamo prima il blocco, lo span adiacente perderà il contesto, .next() lavorerà a partire da un elemento diverso o restituirà una collezione vuota.
Esempio esteso: ricerca case-insensitive
In un progetto reale, il testo dell'etichetta può arrivare con maiuscole/minuscole diverse: featured, Featured, FEATURED. Il selettore standard :contains() non lo gestirà. La soluzione è un selettore personalizzato basato su .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 });
Qui .filter() con una funzione di callback controlla il testo in minuscolo di ogni candidato. Le righe sono leggermente di più, ma lo script non dipende da come esattamente il testo è stato inserito nel pannello di amministrazione.
Alternativa in JavaScript puro
Se jQuery non è proprio usato nel progetto, la stessa logica si scrive in 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 });
Lo stesso algoritmo: iterazione degli holder → ricerca per testo tramite .textContent.includes() → risalita al genitore tramite il nativo .closest() → controllo e rimozione dell'elemento adiacente → rimozione del blocco. L'implementazione nativa è più lunga ma non si trascina dietro la dipendenza jQuery.
Breve video sull'argomento, analisi di :contains() e delle catene di navigazione in jQuery:
⁉️🤔 Domande frequenti
Perché :contains() non trova il testo che vedo sulla pagina?
Molto probabilmente il problema è nelle maiuscole/minuscole o negli spazi.
:contains("Featured")non troverà "featured", è un selettore case-sensitive. Inoltre il browser potrebbe normalizzare gli spazi all'interno dell'HTML in modo diverso da come ti aspetti. Verifica il contenuto esatto tramiteconsole.log($('selector').text())e copia la stringa uno-a-uno in:contains().
Si può fare a meno di .closest() e usare una catena fissa .parent().parent()?
Tecnicamente sì. Praticamente, è sconsigliato. Una catena rigida
.parent().parent()si romperà a ogni modifica dell'annidamento: aggiunto un wrapper, passaggio a un tema diverso, lo script si ferma..closest('a.product_visibility')è più flessibile: cerca l'elemento corrispondente più vicino a qualsiasi livello superiore. Scritto una volta, funziona indipendentemente dalla struttura.
Lo script rimuove lo span sbagliato. Come fare debug?
Primo passo, inserisci
console.log()prima di ogni.remove()e verifica cosa esattamente finisce nella selezione. Secondo, affina il selettore per.next(). Senza un filtro,.next()prende qualsiasi elemento successivo allo stesso livello, anche se è un altro<a>o<div>. Aggiungispan:contains(",")o una classe più specifica e verifica che l'elemento adiacente sia effettivamente quello giusto.
Devo racchiudere lo script in jQuery(document).ready()?
Sì, se lo script è nell'
<head>o si connette prima del rendering del DOM. La costruzionejQuery(document).ready(function($){ ... })garantisce che il codice venga eseguito solo dopo il caricamento completo dell'albero DOM. Inoltre$viene passato esplicitamente nella callback, questo mette al riparo da conflitti con altre librerie che potrebbero anch'esse rivendicare$. Se lo script è nel footer dopo tutti gli elementi,.ready()formalmente non è richiesto, ma la disciplina di «racchiudere sempre» salva da bug difficili da individuare durante il refactoring.
Cosa è più veloce: jQuery o vanilla JS per un task del genere?
JavaScript nativo vince a partenza fredda: nessuna necessità di caricare, analizzare e compilare jQuery, e questo fa risparmiare decine di kilobyte di traffico e diversi millisecondi di parsing. Sulle operazioni DOM, il divario è più modesto, si parla di frazioni di millisecondo per chiamata. Per un tipico sito WordPress dove jQuery è già connesso dal tema o dai plugin, la differenza è impercettibile, il tuo script di quattro righe non diventerà un collo di bottiglia. Se jQuery è già nel progetto, scrivi in jQuery, il codice è più compatto e leggibile. Se non c'è, usa vanilla JS, non importare una libreria per un singolo handler.
Vale la pena usare jQuery per la ricerca testuale nel 2026?
jQuery non è andato da nessuna parte. Secondo le statistiche W3Techs di giugno 2026, la libreria è installata sull'87,3% dei siti con una libreria JavaScript nota (68,1% di tutti i siti), principalmente grazie a WordPress, dove jQuery è nel core e viene connesso automaticamente dai temi. Per uno sviluppatore WordPress, jQuery è uno strumento nativo che è già caricato e non crea richieste extra.
Alternative dirette come Alpine.js o HTMX risolvono task diversi (reattività, aggiornamenti parziali) e sono eccessive per uno script DOM una tantum. Il nativo querySelectorAll + closest() + remove() è un'opzione funzionante senza dipendenze, ma le catene di chiamata si allungano. La scelta si riduce a una regola semplice: jQuery è già nel progetto, prendi jQuery; progetto senza jQuery, prendi vanilla JS.
La cosa principale è che ora conosci l'esatta meccanica della combinazione :contains() + .closest() + .next() + .remove(), e in quale dialetto scriverla lo decide il progetto specifico.



