Skip to content

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

🖱 Scroll suave para âncora: CSS, JavaScript e jQuery - três abordagens funcionais

🖱 Scroll suave para âncora: CSS, JavaScript e jQuery - três abordagens funcionais

O utilizador clica numa ligação com uma âncora e a página salta instantaneamente para o local de destino. Sem animação, sem suavidade, sem noção de onde se aterrou. A transição brusca é desorientadora, especialmente em landing pages extensas e documentação.

Adicionar scroll suave a âncoras demora 5 minutos. Mas o diabo está nos detalhes: a ligação pode vir de um site externo, o URL já contém um hash e há um cabeçalho fixo no topo que cobre a âncora. O scroll-behavior: smooth normal não resolve estas nuances.

Abaixo estão três abordagens funcionais: CSS puro, JavaScript nativo e jQuery. Cada uma com código que pode copiar e colar no seu projeto. Além disso, cobrimos separadamente as transições externas para que o scroll funcione mesmo quando o utilizador chega de outra página ou dos resultados de pesquisa.

💡 Visão geral rápida:

  • Ative o scroll suave com uma linha de CSS, sem qualquer JavaScript
  • Adicione um deslocamento para o cabeçalho fixo usando scroll-margin-top
  • Escreva JS nativo com scrollIntoView para transições externas e ajustes finos
  • Cubra a variante jQuery com animate() quando precisar de velocidade e controlo personalizados
  • Trate o hash externo: página carregada, âncora no URL e scroll suave até ao destino

Por que razão o scroll suave é importante

Saltar abruptamente para uma âncora é desorientador. O utilizador perde o contexto: onde estava, onde aterrou, o que perdeu? O scroll suave mantém a orientação espacial, dando ao olho algo para acompanhar enquanto a página se move.

Do ponto de vista da perceção, um site com scroll suave parece mais polido. Isto é especialmente notório em páginas longas: landing pages, documentação, secções de FAQ, índices de artigos. O utilizador clica em «Preços» no menu e a página não dá um solavanco, mas desliza suavemente até ao bloco de preços.

Tecnicamente, a tarefa divide-se em duas: scroll ao clicar numa ligação interna (o utilizador já está na página) e scroll na transição externa (o URL já contém #anchor). No segundo caso, o navegador, por defeito, salta instantaneamente para a âncora antes mesmo do carregamento total, e isto precisa de ser intercetado.

CSS scroll-behavior, uma linha em vez de um script

A abordagem mais simples e moderna. Os navegadores aprenderam o scroll suave de forma nativa, basta uma regra CSS:

1html {
2 scroll-behavior: smooth;
3}

É tudo. Qualquer transição via ligação de âncora torna-se suave. Sem JavaScript, sem bibliotecas.

Suporte: todos os navegadores modernos desde 2022: Chrome 61+, Firefox 36+, Safari 15.4+, Edge 79+. A única exceção é o IE11, mas a sua quota de tráfego está a aproximar-se de zero.

Cabeçalho fixo: scroll-margin-top

Se um cabeçalho fixo (sticky header) paira sobre o conteúdo, a âncora após o scroll acabará por ficar por baixo dele. Resolve-se com uma linha:

1h2, h3, [id] {
2 scroll-margin-top: 80px; /* header height + offset */
3}

O navegador adicionará automaticamente um deslocamento ao fazer scroll para o elemento alvo. Anteriormente, as pessoas escreviam hacks com padding-top e margin negativa, agora já não é necessário.

Limitações do CSS puro

A abordagem CSS não tem controlo sobre a velocidade da animação e a função de easing. O navegador usa uma curva incorporada, geralmente ease-in-out. Se precisar de velocidade personalizada ou animação não linear, passe para JavaScript.

JavaScript nativo: scrollIntoView

Quando o CSS por si só não é suficiente, o método scrollIntoView() com a opção behavior: 'smooth' vem em socorro:

1document.querySelectorAll('a[href^="#"]').forEach(anchor => {
2 anchor.addEventListener('click', function(e) {
3 e.preventDefault();
4 const target = document.querySelector(this.getAttribute('href'));
5 if (target) {
6 target.scrollIntoView({
7 behavior: 'smooth',
8 block: 'start'
9 });
10 }
11 });
12});

Este código interceta cliques em todas as ligações com #, cancela a transição padrão e lança o scroll suave. Funciona sem jQuery e sem bibliotecas adicionais.

A vantagem: pode controlá-lo programaticamente: alterar a velocidade através do CSS scroll-behavior no <html>, adicionar condições (por exemplo, excluir certas ligações), combinar com scroll-margin-top para o cabeçalho.

A desvantagem: o scrollIntoView não permite definir a velocidade da animação diretamente. Para um tempo de scroll personalizado (digamos, 800 ms em vez dos ~500 ms do navegador), precisa de jQuery ou de requestAnimationFrame manual.

JQuery animate, controlo total sobre a animação

Se o projeto já usa jQuery ou se precisa de ajustes finos (velocidade, deslocamento para cabeçalho, filtragem de ligações), aqui está o script atual. Abaixo está uma versão adaptada da abordagem clássica, reescrita para jQuery moderno:

1jQuery(document).ready(function($) {
2 // Smooth scroll on anchor link click
3 $('a[href*="#"]:not([href="#"])').on('click', function() {
4 if (
5 location.pathname.replace(/^\//, '') === this.pathname.replace(/^\//, '')
6 && location.hostname === this.hostname
7 ) {
8 var target = $(this.hash);
9 target = target.length ? target : $('[name="' + this.hash.slice(1) + '"]');
10 if (target.length) {
11 $('html, body').animate({
12 scrollTop: target.offset().top - 80
13 }, 800);
14 return false;
15 }
16 }
17 });
18
19 // Scroll on external transition with hash in URL
20 if (window.location.hash) {
21 var hash = window.location.hash.substring(1);
22 var $target = $('#' + hash);
23 if ($target.length) {
24 setTimeout(function() {
25 $('html, body').animate({
26 scrollTop: $target.offset().top - 80
27 }, 800);
28 }, 100);
29 }
30 }
31});

O que mudou aqui em comparação com versões antigas que circulam em blogs:

  • .bind() substituído por .on(), o .bind() foi declarado obsoleto no jQuery 3.0 e será removido no jQuery 4.0.
  • Seletor 'a:not(.spu-clickable)[href*="#"]:not([href="#"])' simplificado, a classe específica .spu-clickable estava relacionada com um plugin específico (Popups by OptinMonster) e geralmente não é necessária.
  • jQuery(window).bind("load", ...) removido, a segunda parte do script para hash externo vive agora em $(document).ready() e não requer um evento load separado.
  • Adicionado atraso setTimeout(..., 100) para que o DOM esteja garantidamente renderizado antes de calcular a posição da âncora (relevante para páginas com carregamento lazy de imagens e conteúdo dinâmico).

Onde inserir o código

Três opções à escolha:

  • Através do ficheiro do tema. Adicione o script ao functions.php via wp_enqueue_script(), o método padrão para um tema filho.
  • Plugin Code Snippets. Insira o código como um novo snippet com execução automática, não se perderá na atualização do tema.
  • Plugin de JS personalizado. Se o site já usa um plugin para inserir JavaScript arbitrário (por exemplo, WPCode), adicione o código lá.

Tratamento da transição externa com hash, análise detalhada

A parte mais complicada da tarefa. O utilizador segue uma ligação https://site.com/page/#pricing de outro site ou de uma pesquisa. O navegador, por defeito, salta instantaneamente para #pricing assim que o elemento aparece no DOM, sem animação e muitas vezes antes de a página carregar totalmente.

O script jQuery acima resolve isto em dois passos:

  • Deteta a presença de hash via window.location.hash. Se o hash existir, lê o seu valor (sem #).
  • Encontra o alvo e faz scroll. $('#' + hash) encontra o elemento, setTimeout dá tempo à página para renderizar, animate() conduz ao alvo com um deslocamento de 80 pixels.

O atraso de 100 milissegundos é empírico. Em páginas rápidas, 50 ms são suficientes, em páginas pesadas (landing pages com vídeo de fundo, mapas, gráficos) é melhor aumentar para 200-300 ms. Alternativa: esperar pelo evento window.load em vez de document.ready, mas assim o scroll só acontecerá depois de todas as imagens carregarem, o que pode demorar.

O que fazer se a âncora não encontrar o alvo

Por vezes, o hash no URL aponta para um elemento inexistente. Por exemplo, o utilizador cometeu um erro de digitação ou a página foi alterada. Neste caso, nem a abordagem CSS nem a JS falharão com um erro, simplesmente nada acontecerá. O navegador ignorará a âncora inexistente. O scroll suave também funcionará silenciosamente (a condição if (target.length) não o deixará prosseguir).

Comparação das três abordagens

Critério

CSS scroll-behavior

JS scrollIntoView

jQuery animate

Complexidade de implementação

1 linha CSS

10-15 linhas JS

25-30 linhas JS + jQuery

Controlo de velocidade

Não

Não (padrão do navegador)

Sim (ms)

Hash externo

Sim, nativamente

Requer código extra

Requer código extra

Deslocamento do cabeçalho

scroll-margin-top

scroll-margin-top

Manual em offset().top

Dependências

Nenhuma

Nenhuma

jQuery 3.x

Suporte de navegadores

96%+

96%+

99%+ (com jQuery)

Para a maioria dos projetos modernos, a abordagem CSS é a ideal: uma linha, zero dependências, desempenho nativo. Se precisar de deslocamento para o cabeçalho, adicione scroll-margin-top. Se precisar de velocidade personalizada ou suporte para navegadores antigos, jQuery com animate().

Abaixo está um vídeo com demonstração ao vivo das três abordagens, de CSS a jQuery:

⁉️🤔 Perguntas frequentes

Por que razão o scroll suave não funciona em dispositivos móveis?

No Safari do iOS antes da versão 15.4, o scroll-behavior: smooth não era suportado. Agora já existe suporte (Safari 15.4+, março de 2022). Se o scroll não funcionar em iPhones antigos, adicione um fallback JavaScript com scrollIntoView ou requestAnimationFrame. Verifique também se o scroll não está bloqueado por overflow: hidden no body, alguns menus móveis definem-no ao abrir. Em junho de 2026, o suporte global para scroll-behavior é de 96,3% dos navegadores (dados do Can I Use).

Como fazer scroll suave não para uma âncora, mas para um elemento arbitrário ao clicar num botão?

Atribua um id ao elemento e use o mesmo JavaScript, mas vinculado a um botão em vez de uma ligação de âncora. Por exemplo, botão «Voltar ao topo»: document.querySelector('#back-to-top').addEventListener('click', () => window.scrollTo({ top: 0, behavior: 'smooth' })). Para a variante jQuery: $('#back-to-top').on('click', () => $('html, body').animate({ scrollTop: 0 }, 600)). Sem necessidade de âncoras, faça scroll para coordenadas.

É necessário incluir jQuery apenas para scroll suave em 2026?

Não. O jQuery pesa cerca de 87 KB na versão minificada (30 KB gzip). Se o jQuery não for usado em mais lado nenhum no site, incluí-lo para um scroll é irracional. Adote a abordagem CSS com scroll-behavior: smooth ou scrollIntoView nativo. O jQuery só se justifica se o site já estiver construído sobre ele (temas jQuery, projetos legados) e quiser adicionar scroll à base de código existente.

É possível animar o scroll com velocidade diferente, rápido no início e depois lento?

Sim, através de $.animate() com jQuery UI ou através de requestAnimationFrame com uma função de easing. O animate() padrão do jQuery usa a função linear ou swing. Para curvas não padronizadas, inclua jquery.easing ou escreva o seu próprio loop em requestAnimationFrame com uma função de easing personalizada, por exemplo, easeInOutQuad. Isto dá controlo total sobre a velocidade, trajetória e não requer bibliotecas externas.

Como verificar se o scroll funciona na transição externa com hash?

Abra um novo separador, cole o URL com âncora (por exemplo, https://yoursite.com/page/#contacts) e prima Enter. A página deve carregar e fazer scroll suave até ao bloco. Segunda opção: na consola do navegador, na página aberta, execute location.hash = '#contacts'. Se o scroll for suave, o script funciona. Se a página saltou instantaneamente, o manipulador de hash externo não está configurado.

Que abordagem escolher para a sua tarefa

Se o site é moderno, sem jQuery, o CSS scroll-behavior: smooth cobre a grande maioria dos cenários. Uma linha, resultado instantâneo. Adicione scroll-margin-top para o cabeçalho e está pronto.

Se o site é em WordPress com jQuery no tema, script jQuery com animate(). Dá controlo sobre a velocidade e o deslocamento, trata transições externas, não entra em conflito com outros plugins.

Se está a escrever de raiz e não quer incluir uma biblioteca, scrollIntoView() nativo com manipulador de hash externo. O meio-termo dourado: limpo, rápido, sem dependências.

Pegue no código do artigo, cole-o no seu projeto e verifique a transição externa, este é o único caso que é fácil de esquecer durante os testes.