
🖱 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
scrollIntoViewpara 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:
1 html { 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:
1 h2, 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:
1 document.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:
1 jQuery(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-clickableestava 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 eventoloadseparado.- 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.phpviawp_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,setTimeoutdá 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 |
|
| Manual em |
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: smoothnã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 comscrollIntoViewourequestAnimationFrame. Verifique também se o scroll não está bloqueado poroverflow: hiddennobody, alguns menus móveis definem-no ao abrir. Em junho de 2026, o suporte global parascroll-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
idao 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: smoothouscrollIntoViewnativo. 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 derequestAnimationFramecom uma função de easing. Oanimate()padrão do jQuery usa a função linear ou swing. Para curvas não padronizadas, incluajquery.easingou escreva o seu próprio loop emrequestAnimationFramecom 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, executelocation.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.



