Em 2008, o jQuery era um salvador. Normalizava o caos dos browsers e tornava $('.element').hide() mágico. Em 2026, para o código do teu tema, é dívida técnica: browsers modernos já expõem seletores, eventos, classes e rede nativa com uma API estável.
Carregar uma biblioteca só para alternar uma classe ou fazer um GET JSON é custo de bytes e de parsing que o front não precisa. Este guia ajuda programadores WordPress a migrar o JavaScript do tema de jQuery para Vanilla JS (ES6+), sem fingir que o ecossistema de plugins já abandonou a biblioteca.
Porque o jQuery ainda aparece no WordPress
O WordPress regista jQuery no handle jquery e corre-o em modo noConflict(). O símbolo global fica jQuery, não $, a menos que o teu script envolva o código num IIFE que receba $ como argumento. Isso existe para não partir plugins antigos que assumem esse contrato.
O núcleo e dezenas de plugins listam array( 'jquery' ) em wp_enqueue_script. Enquanto um único consumidor activo pedir o handle, o ficheiro continua a descarregar no front ou no admin. Por isso a regra prática é: migra o teu código primeiro; não faças wp_deregister_script( 'jquery' ) no wp_enqueue_scripts só porque leste um post de performance.
Uma auditoria rápida: abre DevTools, Coverage, recarrega a home e a página de produto. Se o teu theme.js é o único sítio onde $ aparece no front e nenhum plugin enfileira jQuery nessa vista, podes cortar a dependência do tema e medir o delta. Se WooCommerce, um slider ou um formulário ainda pedem o handle, o ficheiro fica - e o teu trabalho passa a ser não acrescentar mais código em cima dele.
Seletores: de $() para querySelector
Para de consultar o DOM com $() no tema novo.
// jQuery
var $btn = $('.btn');
var $container = $('#container');
// Vanilla JS
const btn = document.querySelector('.btn');
const btns = document.querySelectorAll('.btn');
const container = document.getElementById('container');querySelector devolve o primeiro nó ou null. querySelectorAll devolve um NodeList. Não encadeies .addClass().fadeIn() como no jQuery: ou iteras, ou trabalhas num único elemento.
Cuidados práticos no WordPress:
- Guarda o resultado numa constante se o usas mais do que uma vez; não cries o hábito de
$('.menu')em cada handler. - Se o seletor pode falhar (widget opcional, bloco condicional), testa
if (!btn) return;antes de ligar eventos. - Para IDs únicos,
getElementByIdcontinua a ser o caminho mais directo e legível.
Event listeners e delegação
on(), click() e o antigo live() saem. Usa listeners nativos.
document.querySelectorAll('.btn').forEach((btn) => {
btn.addEventListener('click', () => {
// handler
});
});Para elementos injectados depois do DOMContentLoaded (variantes WooCommerce, HTML de um bloco AJAX, linhas de um carrinho), a delegação no ancestral é o padrão estável:
document.addEventListener('click', (event) => {
const target = event.target.closest('.btn-dynamic');
if (!target) {
return;
}
// handler no botão dinâmico
});closest cobre cliques em filhos (ícone SVG dentro do botão) melhor do que matches no event.target cru. No admin e em customizers, confirma que o teu script corre depois do markup que precisas; senão o listener no document ainda funciona, mas um querySelector no load não encontra nada.
Evita misturar jQuery .on('click') e addEventListener no mesmo nó sem documentar a ordem. Dois handlers no mesmo botão duplicam pedidos AJAX e tornam o debug opaco.
Manipulação do DOM e atributos
Classes e estilos inline deixam de passar por .addClass / .css.
el.classList.add('active');
el.classList.remove('hidden');
el.classList.toggle('open');
el.style.color = 'red';
el.setAttribute('aria-expanded', 'true');
el.dataset.panel = 'shipping';Para conteúdo HTML de confiança (templates do teu tema), textContent e innerHTML cobrem o que .text() e .html() faziam. Se o HTML vem de resposta de API, sanitiza no servidor ou usa uma política clara de escaping; não copies o hábito de injectar markup de plugins desconhecidos no front.
Dimensões e visibilidade: em vez de .hide() / .show(), prefer classes CSS (is-hidden com display ou hidden nativo). Assim o estado fica inspeccionável no DOM e o CSS media query não luta com estilos inline do jQuery.
Ajax: de $.ajax para fetch
$.ajax era conveniente; fetch é nativo e baseado em Promises.
async function getData(url) {
const response = await fetch(url, {
credentials: 'same-origin',
headers: {
Accept: 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
}Diferenças que partem migrações apressadas:
fetchnão rejeita a Promise só porque o status é 404 ou 500. Tens de lerresponse.okou o código.- Cookies de sessão WordPress exigem
credentials: 'same-origin'(ouincludeem casos cross-origin controlados). - POST para
admin-ajax.phpprecisa do mesmoaction, nonce e campos que o PHP espera; o formatoFormDataouURLSearchParamssubstitui o objectodatado jQuery. - Para JSON da REST API, envia
X-WP-Noncequando o endpoint exige autenticação de cookie.
Código novo deve preferir a REST API ou handlers dedicados em vez de acumular mais ramos em admin-ajax.php. A migração de um endpoint existente pode ficar em fetch no mesmo URL até reescreveres o backend.
Document ready e módulos
Não precisas de $(document).ready().
document.addEventListener('DOMContentLoaded', () => {
// DOM parseado; imagens e CSS podem ainda carregar
});Se o script está no rodapé com defer, o DOM já está disponível quando corre; o listener continua útil para deixar a ordem explícita. Com type="module" e wp_enqueue_script moderno, podes importar funções por ficheiro e evitar um único theme.js monolítico cheio de seletores globais.
WordPress 6.x encoraja scripts com dependências declaradas e, onde fizer sentido, @wordpress/scripts / interatividade nos blocos. Isso não obriga a React em todo o tema: um ficheiro ES module sem jQuery já é um passo limpo para a maior parte dos temas clássicos.
Como enfileirar sem array( ‘jquery’ )
No functions.php ou num ficheiro de assets do tema:
- Regista o script com
wp_enqueue_script( 'tema-front', ..., array(), VERSION, true )- dependências vazias ou só o que precisares de verdade. - Remove
jqueryda lista de deps do teu handle. - Confirma no HTML gerado que o teu ficheiro já não aparece depois de
jquery.min.jspor causa dessa deps. - Corre smoke tests: menu mobile, formulários, variações de produto, sticky header, qualquer widget que o teu JS tocasse.
Se um pequeno trecho ainda exige jQuery (por exemplo um plugin de terceiros só expõe API em $), isola esse trecho num handle separado que declare a dependência, em vez de puxar jQuery para todo o bundle do tema.
Ordem de migração que não parte produção
Uma sequência que funciona em sites reais:
- Inventário: procura
$,jQuery(ejQuery.só nos ficheiros do tema (não emplugins/). - Substitui seletores e eventos nos módulos sem AJAX.
- Migra chamadas AJAX uma a uma com testes manuais no staging.
- Corta a dependência
jquerydo handle principal. - Mede Lighthouse e Coverage na home e numa URL comercial (checkout, formulário de contacto, single product).
- Só depois avalia se algum plugin órfão ainda justifica o peso no front; isso é decisão de produto, não de um one-liner no
functions.php.
Em lojas WooCommerce, testa variações, fragments do carrinho e checkout. Muitos temas antigos amarram animações de mini-cart a $; a migração falha em silêncio se só testares a home.
Quando ainda faz sentido manter jQuery
Mantém a dependência quando:
- Manténs um plugin próprio que ainda não tem testes para Vanilla JS.
- Um vendor entrega só builds com jQuery e o custo de fork é maior que o custo de bytes.
- O admin (não o front) é o único sítio onde o teu código corre e o jQuery já está carregado pelo núcleo.
Não uses esses casos como desculpa para escrever código novo do tema em $. Código novo em Vanilla JS; legado isolado até haver janela de refatoração.







