
🔍 JQuery: como encontrar um elemento com texto específico
Um programador está a criar um layout de cartão de produto em WooCommerce. Tudo corre conforme o planeado até que chega uma tarefa do painel de administração: «remover o selo "Featured" e a vírgula a seguir, mas apenas para os produtos dentro da categoria "holder"». O processamento manual não é opção, há centenas de produtos. Precisamos de um script que encontre o bloco com o texto "featured" por si só, alcance o elemento pai, agarre o span vizinho e remova toda a construção.
E é aqui que o jQuery entra em cena com os seus encadeamentos: .each() para iteração, .find() para pesquisa, :contains() para filtragem por texto, .closest() para subir ao elemento pai, .next() para passar ao vizinho e .remove() para limpeza. Cinco métodos e a tarefa fica resolvida em quatro linhas. A seguir, detalho cada passo: do seletor à limpeza final, explicando a lógica e as armadilhas.
💡 Resumo rápido:
- Primeiro encontramos o bloco alvo com texto através do seletor contains e subimos ao seu pai através de closest
- Depois passamos ao elemento vizinho através de next e removemos ambos os blocos com precisão através de remove
- No final, depuramos o script e analisamos erros típicos de perda de contexto e next ganancioso
Tarefa: o que procuramos e o que removemos
A estrutura HTML original tem um aspeto aproximado a este:
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>
O objetivo principal é encontrar <span class="tg-item-term"> que contenha o texto featured, subir ao pai <a class="product_visibility">, depois passar ao <span> seguinte (com a vírgula) e removê-lo. Depois disso, remover o <a class="product_visibility"> com o próprio selo. Os outros rótulos (sale, etc.) permanecem intactos.
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 });
O código está deliberadamente dividido em dois encadeamentos em vez de colapsado num só. A razão é simples: após .remove() de um elemento, o encadeamento quebra, não há para onde continuar. Portanto, primeiro limpamos o span vizinho com a vírgula (primeiro encadeamento), depois removemos o próprio bloco (segundo).
Análise passo a passo
Passo 1: entrar em cada holder
1 $('span.tg-cats-holder').each(function() {
O método .each() percorre cada elemento da coleção, no nosso caso todos os <span class="tg-cats-holder"> na página. Se houver vários holders (categorias de produto diferentes), o script funcionará em cada um de forma independente. Dentro da função de callback, this aponta para o holder atual, toda a pesquisa subsequente parte dele.
Passo 2: pesquisar o bloco com texto através de:contains()
1 .find('span.tg-item-term:contains("featured")')
O seletor :contains("featured") encontra um elemento dentro do qual (incluindo nós filhos) existe a substring "featured". Três nuances importantes:
Sensibilidade a maiúsculas/minúsculas. As strings "Featured", "FEATURED" e "featured" são diferentes. Se o selo estiver escrito de forma diferente no painel de administração, adicione uma verificação através de
.filter()com.toLowerCase(), exemplo na secção de FAQ.Pesquisa através de todos os descendentes.
:contains()verifica o conteúdo de texto tanto do próprio elemento como de todos os seus filhos. Se houver outro<strong>dentro de<span class="tg-item-term">, o texto dentro dele também conta.Este não é um seletor de desempenho.
:contains()não é acelerado peloquerySelectorAllnativo, o jQuery percorre todos os candidatos e lê.textContent. Para páginas com centenas de elementos, é melhor restringir a seleção com uma classe ou atributo adicional.
O método .find() limita a pesquisa aos descendentes do holder atual, não à página inteira, o que proporciona um aumento de velocidade e proteção contra falsos positivos.
Passo 3: subir ao pai através de.closest()
1 .closest('a.product_visibility')
.closest() sobe na árvore a partir do elemento encontrado e devolve o ancestral mais próximo que corresponda ao seletor. Ao contrário de .parent(), não está limitado a um nível, sobe até <html> até encontrar uma correspondência ou atingir a raiz.
Se tivéssemos usado .parent() em vez de .closest(), o script quebraria com qualquer aninhamento: <span class="tg-item-term"> → <strong> → <a class="product_visibility">, .parent() devolveria <strong>, não o link necessário. .closest() protege contra essas surpresas.
Passo 4: saltar para o vizinho através de.next()
1 .next('span:contains(",")')
.next() obtém o elemento irmão imediatamente seguinte do mesmo pai. O filtro span:contains(",") garante que obtemos exatamente o span com a vírgula, não qualquer elemento seguinte. Sem o filtro, .next() agarraria o primeiro nó irmão disponível, por exemplo, outro <a class="product_visibility"> com o rótulo seguinte.
A diferença entre .next() e .nextAll(): o primeiro obtém exatamente um elemento, o segundo obtém todos os irmãos subsequentes. Aqui precisamos de um ataque preciso, portanto .next().
Passo 5: remover
1 .remove();
.remove() corta o elemento do DOM completamente, juntamente com os manipuladores de eventos e dados jQuery. Se precisar apenas de ocultar (com a possibilidade de voltar atrás), use .hide() ou .detach(). Mas para a tarefa «remover o selo para sempre», apenas .remove().
A ordem importa: primeiro removemos o span vizinho (primeiro encadeamento), só depois o próprio bloco (segundo). Se removermos o bloco primeiro, o span vizinho perderá o contexto, .next() funcionará a partir de um elemento diferente ou devolverá uma coleção vazia.
Exemplo alargado: pesquisa insensível a maiúsculas/minúsculas
Num projeto real, o texto do rótulo pode vir com capitalização diferente: featured, Featured, FEATURED. O :contains() padrão não lida com isto. A solução é um seletor personalizado baseado em .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 });
Aqui, .filter() com uma função de callback verifica o texto em minúsculas de cada candidato. São ligeiramente mais linhas, mas o script não depende de como exatamente o texto foi introduzido no painel de administração.
Alternativa em JavaScript puro
Se o jQuery não for de todo usado no projeto, a mesma lógica escreve-se em JS vanilla:
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 });
O mesmo algoritmo: iterar holders → pesquisar por texto através de .textContent.includes() → subir ao pai através de .closest() nativo → verificar e remover vizinho → remover bloco. A implementação nativa é mais longa, mas não arrasta uma dependência do jQuery.
Vídeo curto sobre o tema, análise de :contains() e encadeamentos de navegação em jQuery:
⁉️🤔 Perguntas frequentes
Porque é que o :contains() não encontra texto que eu vejo na página?
Muito provavelmente o problema está na capitalização ou nos espaços.
:contains("Featured")não encontrará "featured", este é um seletor sensível a maiúsculas/minúsculas. Além disso, o navegador pode normalizar espaços dentro do HTML de forma diferente do que espera. Verifique o conteúdo exato através deconsole.log($('selector').text())e copie a string tal e qual para:contains().
Podemos dispensar o .closest() e usar um encadeamento fixo .parent().parent()?
Tecnicamente, sim. Na prática, não é recomendado. Um encadeamento rígido
.parent().parent()quebrará com qualquer alteração no aninhamento: adicionou um wrapper, mudou para um tema diferente, o script parou..closest('a.product_visibility')é mais flexível: procura o elemento correspondente mais próximo a qualquer nível acima. Escrito uma vez, funciona independentemente da estrutura.
O script remove o span errado. Como depurar?
Primeiro passo, insira
console.log()antes de cada.remove()e verifique o que exatamente entra na seleção. Segundo, refine o seletor para.next(). Sem um filtro,.next()obtém qualquer elemento seguinte ao mesmo nível, mesmo que seja outro<a>ou<div>. Adicionespan:contains(",")ou uma classe mais específica e verifique se o vizinho é realmente o correto.
Preciso de envolver o script em jQuery(document).ready()?
Sim, se o script estiver no
<head>ou for ligado antes da renderização do DOM. A construçãojQuery(document).ready(function($){ ... })garante que o código é executado apenas após o carregamento completo da árvore DOM. Além disso,$é passado explicitamente para o callback, o que protege contra conflitos com outras bibliotecas que também possam reivindicar$. Se o script estiver no footer depois de todos os elementos,.ready()não é formalmente necessário, mas a disciplina de «envolver sempre» evita bugs difíceis de detetar durante a refatoração.
O que é mais rápido: jQuery ou JS vanilla para uma tarefa destas?
O JavaScript nativo ganha no arranque a frio: não é necessário carregar, analisar e compilar o jQuery, e isto poupa dezenas de kilobytes de tráfego e vários milissegundos de análise. Nas operações de DOM, a diferença é mais modesta, falamos de frações de milissegundo por chamada. Para um site WordPress típico onde o jQuery já está ligado pelo tema ou plugins, a diferença é impercetível, o seu script de quatro linhas não se tornará um gargalo. Se o jQuery já está no projeto, escreva em jQuery, o código é mais compacto e legível. Se não está, use JS vanilla, não arraste uma biblioteca para um único manipulador.
Vale a pena usar jQuery para pesquisa de texto em 2026?
O jQuery não desapareceu. De acordo com as estatísticas da W3Techs de junho de 2026, a biblioteca está instalada em 87,3% dos sites com uma biblioteca JavaScript conhecida (68,1% de todos os sites), principalmente graças ao WordPress, onde o jQuery está no núcleo e é automaticamente ligado pelos temas. Para um programador WordPress, o jQuery é uma ferramenta nativa que já está carregada e não cria pedidos extra.
Alternativas diretas como Alpine.js ou HTMX resolvem tarefas diferentes (reatividade, atualizações parciais) e são excessivas para um script DOM pontual. querySelectorAll + closest() + remove() nativos são uma opção funcional sem dependências, mas os encadeamentos de chamadas tornam-se mais longos. A escolha resume-se a uma regra simples: jQuery já está no projeto, use jQuery; projeto sem jQuery, use JS vanilla.
O principal é que agora conhece a mecânica exata da combinação :contains() + .closest() + .next() + .remove(), e em que dialeto a escrever é decidido pelo projeto específico.



