Rolagem suave ao clicar em um link âncora

487

Eu tenho alguns hiperlinks na minha página. Uma FAQ que os usuários lerão quando visitarem minha seção de ajuda.

Usando os links da âncora, posso fazer a página rolar em direção à âncora e guiar os usuários até lá.

Existe uma maneira de tornar essa rolagem suave?

Mas observe que ele está usando uma biblioteca JavaScript personalizada. Talvez o jQuery ofereça algo como este cozido?

Somente Boliviano Aqui
fonte
Você pode rever a melhor resposta, talvez? Solução css de uma linha pura é difícil de encontrar entre todas as sugestões jQuery volumosos: stackoverflow.com/a/51588820/1422553
Александр Киричек

Respostas:

1160

Atualização em abril de 2018: agora existe uma maneira nativa de fazer isso :

document.querySelectorAll('a[href^="#"]').forEach(anchor => {
    anchor.addEventListener('click', function (e) {
        e.preventDefault();

        document.querySelector(this.getAttribute('href')).scrollIntoView({
            behavior: 'smooth'
        });
    });
});

No momento, isso é suportado apenas nos navegadores mais modernos.


Para suporte mais antigo ao navegador, você pode usar esta técnica jQuery:

$(document).on('click', 'a[href^="#"]', function (event) {
    event.preventDefault();

    $('html, body').animate({
        scrollTop: $($.attr(this, 'href')).offset().top
    }, 500);
});

E aqui está o violino: http://jsfiddle.net/9SDLw/


Se o seu elemento de destino não tiver um ID e você estiver vinculando a ele por ele name, use o seguinte:

$('a[href^="#"]').click(function () {
    $('html, body').animate({
        scrollTop: $('[name="' + $.attr(this, 'href').substr(1) + '"]').offset().top
    }, 500);

    return false;
});

Para aumentar o desempenho, você deve armazenar em cache esse $('html, body')seletor, para que ele não seja executado toda vez que uma âncora for clicada:

var $root = $('html, body');

$('a[href^="#"]').click(function () {
    $root.animate({
        scrollTop: $( $.attr(this, 'href') ).offset().top
    }, 500);

    return false;
});

Se você deseja que o URL seja atualizado, faça-o no animateretorno de chamada:

var $root = $('html, body');

$('a[href^="#"]').click(function() {
    var href = $.attr(this, 'href');

    $root.animate({
        scrollTop: $(href).offset().top
    }, 500, function () {
        window.location.hash = href;
    });

    return false;
});
Joseph Silber
fonte
10
Isso parece remover a # extensão do URL, interrompendo a função back. Existe uma maneira de contornar isso?
Fletch
2
@JosephSilber não deveria ser isso em scrollTop: $(this.hash).offset().topvez de scrollTop: $(this.href).offset().top?
Gregory Pakosz 30/03
4
@CreateSean -scrollTop: $(href).offset().top - 72
Joseph Silber
5
Eu diria que o armazenamento em cache do html, bodyobjeto aqui é desnecessário, executar um seletor uma vez por clique não é muito.
2
Primeira solução é a melhor e mais moderno, você pode usar este polyfill para apoiar este comportamento em navegadores velhos com este polyfill
Efe
166

A sintaxe correta é:

//Smooth scrolling with links
$('a[href*=\\#]').on('click', function(event){     
    event.preventDefault();
    $('html,body').animate({scrollTop:$(this.hash).offset().top}, 500);
});

// Smooth scrolling when the document is loaded and ready
$(document).ready(function(){
  $('html,body').animate({scrollTop:$(location.hash).offset().‌​top}, 500);
});

Simplificando : DRY

function smoothScrollingTo(target){
  $('html,body').animate({scrollTop:$(target).offset().​top}, 500);
}
$('a[href*=\\#]').on('click', function(event){     
    event.preventDefault();
    smoothScrollingTo(this.hash);
});
$(document).ready(function(){
  smoothScrollingTo(location.hash);
});

Explicação de href*=\\#:

  • *significa que ele corresponde ao que contém #char. Assim, combine apenas as âncoras . Para saber mais sobre o significado disso, veja aqui
  • \\é porque o #é um caractere especial no seletor de css, então temos que escapar dele.
Andres Separ
fonte
8
Eu tive que mudar $('a')para $('a[href*=#]')servir apenas urls âncora
okliv
2
@okliv Isso servirá muito, por exemplo, um link javascript como <a href="javascript:$('#test').css('background-color', '#000')">Test</a>. Você deve usar $('a[href^=#]')para combinar todos os URLs que começam com um caractere de hash.
Martin Braun
3
Também, '#' é um carácter especial e ele precisa ser escapado assim:a[href^=\\#]
QuinnFreedman
3
Isso fez com que os links para âncoras em outras páginas parassem de funcionar. Resolvido adicionando uma rolagem suave condicional if ($ ($ (this.hash) .selector) .length) {... suave. }
Liren
1
Como posso animar isso ao viajar para uma nova página? Por exemplo, clicando em: website.com/newpage/#section2. Gostaria que ele carregasse a página e role para baixo. Isso é possível?
Samyer
72

A nova gostosura do CSS3. Isso é muito mais fácil do que todos os métodos listados nesta página e não requer Javascript. Basta digitar o código abaixo no seu css e, de repente, os links apontam para os locais dentro da sua própria página e terão uma animação de rolagem suave.

html{scroll-behavior:smooth}

Depois disso, os links apontados para uma div deslizarão suavemente para essas seções.

<a href="#section">Section1</a>

Edit: Para aqueles confusos sobre o tag acima. Basicamente, é um link clicável. Você pode ter outra tag div em algum lugar da sua página da web, como

<div classname="section">content</div>

Nesse sentido, o link a será clicável e irá para qualquer que seja a seção #s nesse caso, é a nossa div que chamamos seção.

BTW, passei horas tentando fazer isso funcionar. Encontrei a solução em alguma seção de comentários obscuros. Era um buggy e não funcionava em algumas tags. Não funcionou no corpo. Finalmente funcionou quando o coloquei em html {} no arquivo CSS.

Cristian Reyes
fonte
4
Eu posso ser muito útil, mas eles são inconvenientes
Buzut
3
bom, mas tenha cuidado porque no momento em que não é suportado pelo Safari e, obviamente, pelo Explorer (03/2019)
Marco Romano
2
solução agradável, apenas a cobertura é limitada a 74,8%. talvez no futuro
iepur1lla
1
Que incrível. Muito obrigado.
Mikkel Fennefoss 20/08/19
1
Essa será a resposta mais relevante nos próximos anos.
Nurul Huda 13/01
22
$('a[href*=#]').click(function(event){
    $('html, body').animate({
        scrollTop: $( $.attr(this, 'href') ).offset().top
    }, 500);
    event.preventDefault();
});

isso funcionou perfeito para mim

Philipp Sander
fonte
1
"event.preventDefault ();" pode substituir "return false;"
Andres Separ
Desculpe dizer, mas não está funcionando e é exibido na página chamada âncora rapidamente, sem suavidade.
Kamlesh #
18

Estou surpreso que ninguém postou uma solução nativa que também cuida de atualizar o hash do local do navegador para corresponder. Aqui está:

let anchorlinks = document.querySelectorAll('a[href^="#"]')
 
for (let item of anchorlinks) { // relitere 
    item.addEventListener('click', (e)=> {
        let hashval = item.getAttribute('href')
        let target = document.querySelector(hashval)
        target.scrollIntoView({
            behavior: 'smooth',
            block: 'start'
        })
        history.pushState(null, null, hashval)
        e.preventDefault()
    })
}

Consulte o tutorial: http://www.javascriptkit.com/javatutors/scrolling-html-bookmark-javascript.shtml

Para sites com cabeçalhos fixos, o scroll-padding-topCSS pode ser usado para fornecer um deslocamento.

sopros de coco
fonte
1
Eu gosto mais desta resposta. No entanto, não há como fornecer uma compensação. Como seria necessário no caso de um cabeçalho fixo.
bskool
Infelizmente, o mesmo suporte ruim que a propriedade CSS de comportamento de rolagem: developer.mozilla.org/en-US/docs/Web/CSS/…
Dmitry Nevzorov
15

Somente CSS

html {
    scroll-behavior: smooth !important;
}

Tudo o que você precisa adicionar apenas isso. Agora seu comportamento de rolagem de links internos será suave como um fluxo de fluxo.

Nota : Todos os navegadores mais recentes ( Opera, Chrome,Firefox etc) suporte a esse recurso.

para obter detalhes, leia este artigo

WasiF
fonte
1
agradável! Por que essa não é a resposta aceita? não precisamos de todo esse javascript!
Trevor de Koekkoek 23/07/19
1
Funciona muito bem, essa deve ser a resposta aceita.
tomb
Verifique o suporte ao navegador aqui
Ryan Zhou
1
funciona como um encanto. não há necessidade de js
Navbro 04/04
Esta é a melhor solução para rolagem suave NUNCA! Obrigado!
yehanny
10

Eu sugiro que você faça esse código genérico:

$('a[href^="#"]').click(function(){

var the_id = $(this).attr("href");

    $('html, body').animate({
        scrollTop:$(the_id).offset().top
    }, 'slow');

return false;});

Você pode ver um artigo muito bom aqui: jquery-effet-smooth-scroll-defilement-fluide

Sarah Bouou
fonte
9
Isso não é genérico, é o jQuery.
AnrDaemon 30/08/16
6
$(function() {
  $('a[href*=#]:not([href=#])').click(function() {
    if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
      var target = $(this.hash);
      target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
      if (target.length) {
        $('html,body').animate({
          scrollTop: target.offset().top
        }, 1000);
        return false;
      }
    }
  });
});

Oficial: http://css-tricks.com/snippets/jquery/smooth-scrolling/

KingRider
fonte
1
isso só parece funcionar para página interna âncora links, mas links de âncora de outras páginas não funcionam, por exemplo website.com/about-us/#who-we-are
rainerbrunotte
5

Já existem muitas respostas boas aqui - no entanto, todas elas estão faltando o fato de que as âncoras vazias precisam ser excluídas . Caso contrário, esses scripts gerarão erros de JavaScript assim que uma âncora vazia for clicada.

Na minha opinião, a resposta correta é assim:

$('a[href*=\\#]:not([href$=\\#])').click(function() {
    event.preventDefault();

    $('html, body').animate({
        scrollTop: $($.attr(this, 'href')).offset().top
    }, 500);
});
Blackbam
fonte
4

Usando JQuery:

$('a[href*=#]').click(function(){
  $('html, body').animate({
    scrollTop: $( $.attr(this, 'href') ).offset().top
  }, 500);
  return false;
});
brequinn
fonte
3

A resposta dada funciona, mas desativa os links de saída. Abaixo, uma versão com um bônus adicional facilita a saída (swing) e respeita os links de saída.

$(document).ready(function () {
    $('a[href^="#"]').on('click', function (e) {
        e.preventDefault();

        var target = this.hash;
        var $target = $(target);

        $('html, body').stop().animate({
            'scrollTop': $target.offset().top
        }, 900, 'swing', function () {
            window.location.hash = target;
        });
    });
});
Rick
fonte
+1 no stop()entanto, a migalha de URL não funciona como esperado: o botão Voltar não retorna, isso ocorre quando a migalha é definida na URL após a conclusão da animação. É melhor sem uma migalha no URL, por exemplo, é assim que o airbnb faz.
Eric Eric
3

HTML

<a href="#target" class="smooth-scroll">
    Link
</a>
<div id="target"></div>

ou com URL completo absoluto

<a href="https://somewebsite.com/#target" class="smooth-scroll">
    Link
</a>
<div id="target"></div>

jQuery

$j(function() {
    $j('a.smooth-scroll').click(function() {
        if (
                window.location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '')
            &&  window.location.hostname == this.hostname
        ) {
            var target = $j(this.hash);
            target = target.length ? target : $j('[name=' + this.hash.slice(1) + ']');
            if (target.length) {
                $j('html,body').animate({
                    scrollTop: target.offset().top - 70
                }, 1000);
                return false;
            }
        }
    });
});
Jasmeet Singh
fonte
3

Os navegadores modernos estão um pouco mais rápidos atualmente. Um setInterval pode funcionar. Esta função funciona bem no Chrome e Firefox atualmente (um pouco lento no safari, não se incomodou com o IE)

function smoothScroll(event) {
    if (event.target.hash !== '') { //Check if tag is an anchor
        event.preventDefault()
        const hash = event.target.hash.replace("#", "")
        const link = document.getElementsByName(hash) 
        //Find the where you want to scroll
        const position = link[0].getBoundingClientRect().y 
        let top = 0

        let smooth = setInterval(() => {
            let leftover = position - top
            if (top === position) {
                clearInterval(smooth)
            }

            else if(position > top && leftover < 10) {
                top += leftover
                window.scrollTo(0, top)
            }

            else if(position > (top - 10)) {
                top += 10
                window.scrollTo(0, top)
            }

        }, 6)//6 milliseconds is the faster chrome runs setInterval
    }
}
pequenas botas
fonte
3

Existe uma maneira CSS de fazer isso usando o comportamento de rolagem. Adicione a seguinte propriedade.

    scroll-behavior: smooth;

E é isso. Não é necessário JS.

a {
  display: inline-block;
  width: 50px;
  text-decoration: none;
}
nav, scroll-container {
  display: block;
  margin: 0 auto;
  text-align: center;
}
nav {
  width: 339px;
  padding: 5px;
  border: 1px solid black;
}
scroll-container {
  display: block;
  width: 350px;
  height: 200px;
  overflow-y: scroll;
  scroll-behavior: smooth;
}
scroll-page {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  font-size: 5em;
}
<nav>
  <a href="#page-1">1</a>
  <a href="#page-2">2</a>
  <a href="#page-3">3</a>
</nav>
<scroll-container>
  <scroll-page id="page-1">1</scroll-page>
  <scroll-page id="page-2">2</scroll-page>
  <scroll-page id="page-3">3</scroll-page>
</scroll-container>

PS: verifique a compatibilidade do navegador.

Santosh
fonte
para qual contêiner devo usar o comportamento de rolagem: suave;
CraZyDroiD
Em caso de dúvida, adicione-o à tag body @CraZyDroiD
Santosh
2

Adicionando isso:

function () {
    window.location.hash = href;
}

está de alguma forma anulando o deslocamento vertical

top - 72

no Firefox e IE, não no Chrome. Basicamente, a página rola suavemente até o ponto em que deve parar com base no deslocamento, mas depois pula para onde a página iria sem o deslocamento.

Ele adiciona o hash ao final do URL, mas pressionar para trás não o leva de volta ao topo, apenas remove o hash do URL e deixa a janela de visualização onde fica.

Aqui está o js completo que estou usando:

var $root = $('html, body');
$('a').click(function() {
    var href = $.attr(this, 'href');
    $root.animate({
        scrollTop: $(href).offset().top - 120
    }, 500, function () {
        window.location.hash = href;
    });
    return false;
});
Reid
fonte
2

Esta solução também funcionará para os seguintes URLs, sem quebrar os links âncora para páginas diferentes.

http://www.example.com/dir/index.html
http://www.example.com/dir/index.html#anchor

./index.html
./index.html#anchor

etc.

var $root = $('html, body');
$('a').on('click', function(event){
    var hash = this.hash;
    // Is the anchor on the same page?
    if (hash && this.href.slice(0, -hash.length-1) == location.href.slice(0, -location.hash.length-1)) {
        $root.animate({
            scrollTop: $(hash).offset().top
        }, 'normal', function() {
            location.hash = hash;
        });
        return false;
    }
});

Ainda não testei isso em todos os navegadores.

Midas
fonte
2

Isso facilitará o jQuery discernir o hash de destino e saber quando e onde parar.

$('a[href*="#"]').click(function(e) {
    e.preventDefault();
    var target = this.hash;
    $target = $(target);

    $('html, body').stop().animate({
        'scrollTop': $target.offset().top
    }, 900, 'swing', function () {
        window.location.hash = target;
    });
});
PanicBus
fonte
2
$("a").on("click", function(event){
    //check the value of this.hash
    if(this.hash !== ""){
        event.preventDefault();

        $("html, body").animate({scrollTop:$(this.hash).offset().top}, 500);

        //add hash to the current scroll position
        window.location.hash = this.hash;

    }



});
Abk
fonte
2

Código testado e verificado

<script>
jQuery(document).ready(function(){
// Add smooth scrolling to all links
jQuery("a").on('click', function(event) {

// Make sure this.hash has a value before overriding default behavior
if (this.hash !== "") {
  // Prevent default anchor click behavior
  event.preventDefault();

  // Store hash
  var hash = this.hash;

  // Using jQuery's animate() method to add smooth page scroll
  // The optional number (800) specifies the number of milliseconds it takes to scroll to the specified area
  jQuery('html, body').animate({
    scrollTop: jQuery(hash).offset().top
  }, 800, function(){

    // Add hash (#) to URL when done scrolling (default click behavior)
    window.location.hash = hash;
  });
} // End if
});
});
</script>
Atif Tariq
fonte
1

Eu fiz isso para as âncoras href "/ xxxxx # asdf" e "#asdf"

$("a[href*=#]").on('click', function(event){
    var href = $(this).attr("href");
    if ( /(#.*)/.test(href) ){
      var hash = href.match(/(#.*)/)[0];
      var path = href.match(/([^#]*)/)[0];

      if (window.location.pathname == path || path.length == 0){
        event.preventDefault();
        $('html,body').animate({scrollTop:$(this.hash).offset().top}, 1000);
        window.location.hash = hash;
      }
    }
});
AndreDurao
fonte
1

Aqui está a solução que eu implementei para vários links e âncoras, para uma rolagem suave:

http://www.adriantomic.se/development/jquery-localscroll-tutorial/ se você tiver seus links de navegação configurados em uma divisão de navegação e declarados com esta estrutura:

<a href = "#destinationA">

e seus destinos de tag de âncora correspondentes da seguinte maneira:

<a id = "destinationA">

Em seguida, basta carregar isso no cabeçalho do documento:

    <!-- Load jQuery -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>

<!-- Load ScrollTo -->
<script src="http://flesler-plugins.googlecode.com/files/jquery.scrollTo-1.4.2-min.js"></script>

<!-- Load LocalScroll -->
<script src="http://flesler-plugins.googlecode.com/files/jquery.localscroll-1.2.7-min.js"></script>

<script type = "text/javascript">
 $(document).ready(function()
    {
        // Scroll the whole document
        $('#menuBox').localScroll({
           target:'#content'
        });
    });
</script>

Obrigado a @Adriantomic

collyg
fonte
1

Se você tiver um botão simples na página para rolar para baixo até uma div e quiser que o botão voltar funcione pulando para o topo, basta adicionar este código:

$(window).on('hashchange', function(event) {
    if (event.target.location.hash=="") {
        window.scrollTo(0,0);
    }
});

Isso também pode ser estendido para pular para divs diferentes, lendo o valor do hash e rolando como Joseph Silbers responde.

Niclas
fonte
1

Nunca esqueça que a função offset () está dando a posição do seu elemento para documentar. Portanto, quando você precisar rolar seu elemento em relação ao pai, use isso;

    $('.a-parent-div').find('a').click(function(event){
        event.preventDefault();
        $('.scroll-div').animate({
     scrollTop: $( $.attr(this, 'href') ).position().top + $('.scroll-div').scrollTop()
     }, 500);       
  });

O ponto principal é obter o scrollTop de scroll-div e adicioná-lo ao scrollTop. Se você não fizer essa função position (), sempre fornecerá valores de posição diferentes.

rotring05
fonte
1

Você pode usar window.scroll()com behavior: smoothe topdefinir a parte superior deslocada da marca de âncora, o que garante que a marca de âncora esteja na parte superior da janela de visualização.

document.querySelectorAll('a[href^="#"]').forEach(a => {
    a.addEventListener('click', function (e) {
        e.preventDefault();
        var href = this.getAttribute("href");
        var elem = document.querySelector(href)||document.querySelector("a[name="+href.substring(1, href.length)+"]");
        //gets Element with an id of the link's href 
        //or an anchor tag with a name attribute of the href of the link without the #
        window.scroll({
            top: elem.offsetTop, 
            left: 0, 
            behavior: 'smooth' 
        });
        //if you want to add the hash to window.location.hash
        //you will need to use setTimeout to prevent losing the smooth scrolling behavior
       //the following code will work for that purpose
       /*setTimeout(function(){
            window.location.hash = this.hash;
        }, 2000); */
    });
});

Demo:

Você pode simplesmente definir a propriedade CSS scroll-behaviorcomo smooth(suportada pela maioria dos navegadores modernos), o que evita a necessidade de Javascript.

hev1
fonte
0

obrigado por compartilhar, Joseph Silber. Aqui, sua solução de 2018 como ES6 com uma pequena alteração para manter o comportamento padrão (role para cima):

document.querySelectorAll("a[href^=\"#\"]").forEach((anchor) => {
  anchor.addEventListener("click", function (ev) {
    ev.preventDefault();

    const targetElement = document.querySelector(this.getAttribute("href"));
    targetElement.scrollIntoView({
      block: "start",
      alignToTop: true,
      behavior: "smooth"
    });
  });
});
Motine
fonte
0

Requer jquery e anima para ancorar a tag com o nome especificado em vez de id, enquanto adiciona o hash ao URL do navegador. Também corrige um erro na maioria das respostas com jquery, onde o sinal # não é prefixado com uma barra invertida de escape. O botão voltar, infelizmente, não retorna corretamente aos links hash anteriores ...

$('a[href*=\\#]').click(function (event)
{
    let hashValue = $(this).attr('href');
    let name = hashValue.substring(1);
    let target = $('[name="' + name + '"]');
    $('html, body').animate({ scrollTop: target.offset().top }, 500);
    event.preventDefault();
    history.pushState(null, null, hashValue);
});
jjxtra
fonte