Skip to content

Tudo para WordPress, desenvolvimento web — e não só

🔧 JQuery: scroll suave para âncora ao navegar a partir de outra página

🔧 JQuery: scroll suave para âncora ao navegar a partir de outra página

Adicionou uma âncora à página, colocou #section no URL e espera um scroll suave e agradável. O utilizador clica a partir de outra página, o navegador aplica o hash obedientemente... e salta abruptamente para o destino. Sem scroll. Apenas um salto instantâneo.

O problema é que os navegadores tratam o hash instantaneamente por defeito, antes de o DOM estar totalmente pronto e de os scripts terem oportunidade de intercetar o evento. Quando um utilizador chega através de um link externo, o seu handler de click nunca chega a disparar: não houve evento de clique, apenas uma navegação direta por URL.

Compilámos duas soluções jQuery testadas em cenários reais: um script básico para casos simples e uma versão avançada com tratamento de cliques, suporte para carregamento de página e prevenção de conflitos. Ambas foram testadas em projetos reais e funcionam com as versões atuais do jQuery.

💡 Visão geral rápida:

  • Guarde o hash do URL numa variável temporária
  • Limpe location.hash para que o navegador não salte abruptamente
  • Faça scroll até à âncora via animate() com o deslocamento desejado
  • Restaure o hash para ter um URL correto na barra de endereço

Porque é que o scroll suave para âncoras é importante

Quando um visitante segue um link como site.com/page/#pricing, espera ver o bloco de preços, não o topo da página. Um salto abrupto é desorientador: o utilizador não percebe onde aterrou nem o que veio antes. O scroll suave fornece contexto: os olhos captam as secções intermédias e o cérebro mapeia o percurso da origem ao destino.

Com a propriedade nativa de CSS scroll-behavior: smooth, as coisas são simples: adicione-a à sua folha de estilos e o navegador faz scroll até à âncora automaticamente. Mas esta abordagem deixa de funcionar no momento em que precisa de um deslocamento personalizado para um cabeçalho fixo. Um deslocamento de 110 píxeis impede que o bloco deslize para debaixo do header e posiciona-o exatamente onde o utilizador espera. A solução jQuery dá-lhe esse controlo.

Há também a questão da compatibilidade entre navegadores. scroll-behavior: smooth não funciona no Internet Explorer (sim, esses projetos ainda existem), enquanto o método animate() do jQuery se comporta de forma idêntica em todo o lado.

Script básico: código mínimo, benefício máximo

A primeira opção é compacta, apenas 10 linhas. Resolve exatamente um problema: fazer scroll suave até uma âncora quando o utilizador chega de outra página ou através de um link direto com hash.

1jQuery(document).ready(function ($) {
2 var myHash = location.hash; // save the hash
3 location.hash = ''; // clear it — browser stops jerking
4 if (myHash[1] != undefined) { // hash not empty?
5 $('html, body').animate(
6 { scrollTop: $(myHash).offset().top - 110 },
7 700 // duration in milliseconds
8 );
9 location.hash = myHash; // restore the hash
10 }
11});

Explicação passo a passo, porque três linhas fazem magia, mas compreender a mecânica é importante:

  • var myHash = location.hash: lê o hash da barra de endereço (por exemplo, #pricing). Neste momento, o navegador ainda não fez scroll.
  • location.hash = '': o truque principal. Limpar o hash faz com que o navegador perca o alvo para o salto instantâneo. Sem esta linha, o script «lutaria» contra o comportamento nativo do navegador e perderia.
  • $(myHash).offset().top - 110: calcula a posição da âncora a partir do topo do documento e subtrai 110 píxeis. Este é o seu deslocamento para um cabeçalho fixo. Se o seu cabeçalho tiver position: fixed e 80px de altura, use 80 (ou 90 para dar alguma margem). Se não houver cabeçalho, remova completamente a subtração.
  • $('html, body').animate(...): faz scroll suave durante 700 milissegundos. O seletor 'html, body' não é um capricho: navegadores diferentes fazem scroll ou no html ou no body, por isso especificamos ambos para fiabilidade.
  • location.hash = myHash: restaura o hash. Agora a barra de endereço mostra novamente #pricing e a página já está confortavelmente estacionada no bloco de destino.

Insira o script no functions.php do seu tema, num ficheiro JS personalizado ou através de um plugin como Code Snippets. O requisito principal é que deve estar dentro de um bloco jQuery(document).ready().

Versão avançada: tratamento de cliques, carregamento de página e proteção contra conflitos

O script básico cobre a maioria dos cenários, mas por vezes location.hash = '' não funciona a tempo: o navegador salta antes de o hash ser limpo. Nesses casos, use a segunda versão. É mais pesada, mas mais fiável.

1jQuery(document).ready(function () {
2 jQuery(window).bind('load', function () {
3 jQuery('a:not(.spu-clickable)[href*="#"]:not([href="#"])').click(function () {
4 if (
5 location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') ||
6 location.hostname == this.hostname
7 ) {
8 var target = jQuery(this.hash);
9 target = target.length ? target : jQuery('[name=' + this.hash.slice(1) + ']');
10 if (target.length) {
11 jQuery('html, body').animate(
12 { scrollTop: target.offset().top - 37 },
13 1000
14 );
15 return false;
16 }
17 }
18 });
19 });
20});
21
22jQuery(window).load(function () {
23 function goToByScroll(id) {
24 jQuery('html, body').animate(
25 { scrollTop: jQuery('#' + id).offset().top - 38 },
26 1000
27 );
28 }
29 if (window.location.hash != '') {
30 goToByScroll(window.location.hash.substr(1));
31 }
32});

Eis o que acontece, bloco a bloco:

Bloco um: um handler de clique para links de âncora internos. jQuery(window).bind('load', ...) garante que todos os elementos da página (incluindo imagens e iframes) são carregados antes de vincular o handler. O filtro a:not(.spu-clickable) exclui links já tratados por um plugin de popup (SPU, WordPress PopUp), prevenindo conflitos de animação. Adicione as suas próprias classes de exclusão seguindo o mesmo padrão.

Bloco dois: tratamento da navegação a partir de uma página externa. jQuery(window).load() dispara mais tarde do que document.ready e garante que todos os elementos, incluindo a âncora, estão no DOM. A função goToByScroll(id) faz exatamente o que o script básico faz, mas só é chamada depois de a janela carregar completamente. substr(1) remove o caractere # do hash.

Nota importante: jQuery(window).load() foi descontinuado desde o jQuery 3.0. Se o seu projeto usar uma versão atual do jQuery, substitua por:

1jQuery(window).on('load', function () {
2 // code here
3});

A sintaxe difere, mas o comportamento é idêntico.

Afinação: deslocamentos, seletores e armadilhas

O script funciona, mas cada layout é único. Eis três ajustes comuns que salvaram projetos reais.

Problema um: offset().top devolve coordenadas incorretas. Isto acontece quando elementos pai têm as propriedades CSS transform, filter ou will-change, que criam um novo contexto de posicionamento. O offset() calcula então em relação a esse contexto em vez do documento. Solução: use offset({top: -0}):

1jQuery('html, body').animate(
2 { scrollTop: jQuery('#' + id).offset({ top: -0 }).top },
3 1000
4);

O parâmetro {top: -0} força o jQuery a recalcular a posição a partir dos limites do documento, ignorando contextos intermédios. Parece magia, mas funciona.

Problema dois: o seletor 'html, body' faz scroll no elemento errado. Em alguns temas, o contentor de scroll não é html nem body, mas um bloco específico, por exemplo, body.home para a página inicial ou .main-content para páginas internas. Identifique o seu contentor através das DevTools (separador Elements → procure por overflow: scroll ou overflow: auto) e substitua-o no seletor:

1jQuery('body.home').animate(
2 { scrollTop: target.offset().top - 37 },
3 1000
4);

Problema três: o hash contém caracteres cirílicos ou especiais. location.hash devolve uma sequência codificada em URL em vez de uma string legível, e o seletor jQuery $(myHash) não encontrará o elemento. Solução: descodifique o hash com decodeURIComponent() antes de o usar no seletor.

Uma demonstração curta de John Smith: scroll suave para âncora via animate() do jQuery numa página real com cabeçalho fixo e navegação externa.

⁉️🤔 Perguntas frequentes

O script funciona na mesma página, mas não quando se navega a partir de outro URL. Porquê?

Porque o handler de click nunca dispara: não houve clique. O utilizador seguiu um link direto e o navegador processou o hash antes de o seu JS ser executado. A solução é a sequência location.hash = '' + animate() + location.hash = myHash. Limpar o hash cancela o salto nativo, a animação faz scroll suave e restaurar o hash preserva o URL correto na barra de endereço.

Que deslocamento devo usar para um cabeçalho fixo?

A altura exata do cabeçalho mais alguns píxeis de margem. Abra as DevTools (F12), selecione o elemento header do seu site e verifique a sua height no separador Computed. Por exemplo, se o cabeçalho tiver 80px, use 90 no seu código. Se a versão móvel tiver uma altura de cabeçalho diferente, adicione uma media query com um cálculo de deslocamento dinâmico.

O jQuery ainda é relevante em 2026?

Sim, especialmente no ecossistema WordPress. O jQuery permanece no núcleo do WP, muitos plugins e temas dependem dele, e migrar todo o ecossistema para JS nativo levará anos. Para novos projetos em JavaScript puro, o scroll suave faz-se via element.scrollIntoView({ behavior: 'smooth' }) ou scroll-behavior: smooth em CSS, com significativamente menos linhas de código. Mas se estiver a modificar um site WP existente, a solução jQuery continua a ser fiável e adequada.

Posso evitar completamente o jQuery?

Pode e deve, se o projeto for novo. O equivalente nativo do script básico:

1document.addEventListener('DOMContentLoaded', () => {
2 const hash = window.location.hash;
3 if (hash) {
4 window.location.hash = '';
5 const target = document.querySelector(hash);
6 if (target) {
7 window.scrollTo({
8 top: target.getBoundingClientRect().top + window.pageYOffset - 110,
9 behavior: 'smooth'
10 });
11 }
12 window.location.hash = hash;
13 }
14});

Mesmo princípio: guarde o hash, limpe-o, faça scroll, restaure-o. A diferença é que behavior: 'smooth' usa o motor CSS nativo do navegador em vez de um temporizador jQuery. Não pode configurar a velocidade, mas o código é 30 KB mais leve (o peso do jQuery).

Porquê $('html, body') em vez de apenas $('html')?

O Firefox faz scroll no html, o Chrome usa o body e o Safari depende da versão. Especificar ambos os seletores no animate() garante que funciona em todos os navegadores: o jQuery envia a animação para o elemento que realmente a aplicar.

Vale a pena usar jQuery apenas para scroll suave em 2026

Se tem um site WordPress ou um projeto legado onde o jQuery já está carregado, aproveite o script deste artigo e não complique. Dez linhas de código que funcionam em todo o lado, em qualquer navegador, sem necessidade de polyfills. Para novos projetos em JavaScript puro, use o nativo scroll-behavior: smooth ou scrollIntoView(). Ambas as abordagens produzem scroll suave, mas o método jQuery dá-lhe controlo sobre a velocidade, o deslocamento e o seletor do contentor de scroll. As implementações nativas exigem verificações separadas para alcançar o mesmo.

A regra principal que aprendemos em dezenas de projetos: limpe sempre o hash antes de fazer scroll. Sem este truque, nenhum handler consegue vencer o comportamento nativo do navegador e os utilizadores continuarão a sofrer saltos abruptos.