Rolagem saltando em seus sites

Publicados: 2022-03-10
Resumo rápido ↬ Este artigo descreve o efeito do salto de rolagem e como ele funciona em diferentes navegadores da web. Ele contém análises de várias soluções diferentes sugeridas na Web que podem ser usadas para evitar o salto de rolagem. A propriedade CSS, overscroll-behavior , que foi implementada no Chrome em dezembro de 2017 e no Firefox em março de 2018, também é descrita neste artigo. Uma boa compreensão desse efeito é muito útil para construir ou projetar qualquer site que tenha elementos fixos.

O salto de rolagem (também conhecido como rolagem 'rubber-banding' ou 'rolagem elástica') é frequentemente usado para se referir ao efeito que você vê quando rola para o topo de uma página ou elemento HTML, ou para a parte inferior de uma página ou elemento, em um dispositivo usando uma tela sensível ao toque ou um trackpad, e o espaço vazio pode ser visto por um momento antes que o elemento ou a página volte e se alinhe de volta à parte superior/inferior (quando você soltar o toque/dedos). Você pode ver um efeito semelhante acontecer no encaixe de rolagem CSS entre os elementos.

No entanto, este artigo se concentra no salto de rolagem quando você rola para a parte superior ou inferior de uma página da web. Em outras palavras, quando a porta de rolagem atingiu seu limite de rolagem.

Coletando dados, a maneira poderosa

Você sabia que o CSS pode ser usado para coletar estatísticas? De fato, existe até uma abordagem somente CSS para rastrear interações de interface do usuário usando o Google Analytics. Leia um artigo relacionado →

Uma boa compreensão do salto de rolagem é muito útil, pois ajudará você a decidir como construir seus sites e como deseja que a página role.

O salto de rolagem é indesejável se você não quiser ver elementos fixed em uma movimentação de página. Alguns exemplos incluem: quando você deseja que um cabeçalho ou rodapé seja fixado em uma determinada posição, ou se você deseja que qualquer outro elemento, como um menu, seja fixado, ou se você deseja que a página role-snap em determinadas posições na rolagem e você não deseja que nenhuma rolagem adicional ocorra na parte superior ou inferior da página, o que confundirá os visitantes do seu site. Este artigo irá propor algumas soluções para os problemas enfrentados ao lidar com o salto de rolagem na parte superior ou inferior de uma página da web.

Mais depois do salto! Continue lendo abaixo ↓

Meu primeiro encontro com o efeito

Eu notei esse efeito pela primeira vez quando estava atualizando um site que construí há muito tempo. Você pode ver o site aqui. O rodapé na parte inferior da página deveria ser fixado em sua posição na parte inferior da página e não se mover. Ao mesmo tempo, você deveria poder rolar para cima e para baixo pelo conteúdo principal da página. O ideal seria funcionar assim:

Rolagem saltando no Firefox no macOS
Rolagem saltando no Firefox no macOS. (Visualização grande)

Atualmente, funciona dessa maneira no Firefox ou em qualquer navegador em um dispositivo sem tela sensível ao toque ou trackpad. No entanto, naquela época, eu estava usando o Chrome em um MacBook. Eu estava rolando até o final da página usando um trackpad quando descobri que meu site não estava funcionando corretamente. Você pode ver o que aconteceu aqui:

Rolagem saltando no Chrome no macOS
Rolagem saltando no Chrome no macOS. (Visualização grande)

Ah não! Não era isso que deveria acontecer! Eu tinha definido a posição do rodapé para estar na parte inferior da página, definindo sua propriedade de position CSS para ter um valor de fixed . Este também é um bom momento para revisitar qual position: fixed; é. De acordo com a especificação CSS 2.1, quando uma “caixa” (neste caso, o rodapé azul escuro) é fixa, ela é “fixa em relação à janela de visualização e não se move quando rolada”. O que isso significa é que o rodapé não deveria se mover quando você rola para cima e para baixo na página. Foi isso que me preocupou quando vi o que estava acontecendo no Chrome.

Para tornar este artigo mais completo, mostrarei como a página rola no Mobile Edge, no Mobile Safari e no Desktop Safari abaixo. Isso é diferente do que acontece na rolagem no Firefox e no Chrome. Espero que isso lhe dê uma melhor compreensão de como o mesmo código funciona atualmente de maneiras diferentes. Atualmente, é um desafio desenvolver rolagem que funcione da mesma maneira em diferentes navegadores da web.

Rolagem saltando no Safari no macOS. Um efeito semelhante pode ser visto para Edge e Safari no iOS
Rolagem saltando no Safari no macOS. Um efeito semelhante pode ser visto para Edge e Safari no iOS. (Visualização grande)

Procurando uma solução

Um dos meus primeiros pensamentos foi que haveria uma maneira fácil e rápida de corrigir esse problema em todos os navegadores. O que isso significa é que eu pensei que poderia encontrar uma solução que levaria algumas linhas de código CSS e que nenhum JavaScript estaria envolvido. Portanto, uma das primeiras coisas que fiz, foi tentar conseguir isso. Os navegadores que usei para testar incluíram Chrome, Firefox e Safari no macOS e Windows 10 e Edge e Safari no iOS. As versões desses navegadores eram as mais recentes no momento da redação deste artigo (2018).

Soluções somente HTML e CSS

Posicionamento Absoluto e Relativo

Uma das primeiras coisas que tentei foi usar o posicionamento absoluto e relativo para posicionar o rodapé porque estava acostumado a construir rodapés assim. A ideia seria definir minha página da web para 100% de altura para que o rodapé fique sempre na parte inferior da página com uma altura fixa, enquanto o conteúdo ocupa 100% menos a altura do rodapé e você pode rolar por isso. Como alternativa, você pode definir um padding-bottom vez de usar calc e definir a altura body-container para 100% para que o conteúdo do aplicativo não se sobreponha ao rodapé. O código CSS ficou assim:

 html { width: 100%; height: 100%; overflow: hidden; position: relative; } body { width: 100%; margin: 0; font-family: sans-serif; height: 100%; overflow: hidden; } .body-container { height: calc(100% - 100px); overflow: auto; } .color-picker-main-container { width: 100%; font-size: 22px; padding-bottom: 10px; } footer { position: absolute; bottom: 0; height: 100px; width: 100%; }

Esta solução funciona quase da mesma forma que a solução original (que era apenas position: fixed; ). Uma vantagem dessa solução em relação a ela é que a rolagem não é para a página inteira, mas apenas para o conteúdo da página sem o rodapé. O maior problema com esse método é que no Mobile Safari, tanto o rodapé quanto o conteúdo do aplicativo se movem ao mesmo tempo. Isso torna essa abordagem muito problemática ao rolar rapidamente:

Posicionamento Absoluto e Relativo
Posicionamento Absoluto e Relativo.

Outro efeito que eu não queria foi difícil de perceber no início, e só percebi que estava acontecendo depois de experimentar mais soluções. Isso foi um pouco mais lento para percorrer o conteúdo do meu aplicativo. Como estamos definindo a altura do nosso contêiner de rolagem para 100% de si mesmo, isso dificulta a rolagem baseada em movimento/momento no iOS. Se essa altura de 100% for menor (por exemplo, quando uma altura de 100% de 2000px se torna uma altura de 100% de 900px), a rolagem baseada em momento fica pior. A rolagem baseada em movimento/momento acontece quando você passa os dedos na superfície de uma tela sensível ao toque e a página rola sozinha. No meu caso, eu queria que a rolagem baseada em momento ocorresse para que os usuários pudessem rolar rapidamente, então fiquei longe de soluções que definem uma altura de 100%.

Outras tentativas

Uma das soluções sugeridas na web, e que tentei usar no meu código, é mostrada abaixo como exemplo.

 html { width: 100%; position: fixed; overflow: hidden; } body { width: 100%; margin: 0; font-family: sans-serif; position: fixed; overflow: hidden; } .body-container { width: 100vw; height: calc(100vh - 100px); overflow-y: auto; -webkit-overflow-scrolling: touch; } .color-picker-main-container { width: 100%; font-size: 22px; padding-bottom: 10px; } footer { position: fixed; bottom: 0; height: 100px; width: 100%; }

Este código funciona no Chrome e Firefox no macOS da mesma forma que a solução anterior. Uma vantagem desse método é que a rolagem não está restrita a 100% de altura, portanto, a rolagem baseada em momento funciona corretamente. No Safari, no entanto, o rodapé desaparece:

Falta o rodapé no macOS Safari
Falta o rodapé no macOS Safari. (Visualização grande)

No iOS Safari, o rodapé fica mais curto e há uma lacuna extra transparente (ou branca) na parte inferior. Além disso, a capacidade de rolar pela página é perdida depois que você rola até o final. Você pode ver a lacuna em branco abaixo do rodapé aqui:

Rodapé mais curto no iOS Safari
Rodapé mais curto no iOS Safari.

Uma linha de código interessante que você pode ver muito é: -webkit-overflow-scrolling: touch; . A ideia por trás disso é que ele permite a rolagem baseada em momento para um determinado elemento. Esta propriedade é descrita como “não padrão” e como “não em uma trilha padrão” na documentação do MDN. Ele aparece como um “valor de propriedade inválido” sob inspeção no Firefox e no Chrome, e não aparece como uma propriedade no Desktop Safari. Eu não usei essa propriedade CSS no final.

Para mostrar outro exemplo de uma solução que você pode encontrar e um resultado diferente que encontrei, também tentei o código abaixo:

 html { position: fixed; height: 100%; overflow: hidden; } body { font-family: sans-serif; margin: 0; width: 100vw; height: 100vh; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; } .color-picker-main-container { width: 100%; font-size: 22px; padding-bottom: 110px; } footer { position: fixed; }

Isso realmente funciona bem em diferentes navegadores de desktop, a rolagem baseada em momento ainda funciona e o rodapé é fixado na parte inferior e não se move nos navegadores da web de desktop. Talvez a parte mais problemática desta solução (e o que a torna única) é que, no iOS Safari, o rodapé sempre treme e distorce um pouco e você pode ver o conteúdo abaixo dele sempre que rolar.

Soluções com JavaScript

Depois de experimentar algumas soluções iniciais usando apenas HTML e CSS, experimentei algumas soluções JavaScript. Gostaria de acrescentar que isso é algo que eu não recomendo que você faça e seria melhor evitar fazer. Pela minha experiência, geralmente existem soluções mais elegantes e concisas usando apenas HTML e CSS. No entanto, eu já tinha passado muito tempo experimentando as outras soluções, achei que não faria mal ver rapidamente se havia algumas soluções alternativas que utilizavam JavaScript.

Eventos de toque

Uma abordagem para resolver o problema do salto de rolagem é impedir os eventos touchmove ou touchstart na window ou no document . A ideia por trás disso é que os eventos de toque na janela geral são impedidos, enquanto os eventos de toque no conteúdo pelo qual você deseja rolar são permitidos. Um exemplo de código como este é mostrado abaixo:

 // Prevents window from moving on touch on older browsers. window.addEventListener('touchmove', function (event) { event.preventDefault() }, false) // Allows content to move on touch. document.querySelector('.body-container').addEventListener('touchmove', function (event) { event.stopPropagation() }, false)

Eu tentei muitas variações deste código para tentar fazer o scroll funcionar corretamente. Impedir o touchmove na window não fez diferença. Usar document não fez diferença. Também tentei usar o touchstart e o touchmove para controlar a rolagem, mas esses dois métodos também não fizeram diferença. Eu aprendi que você não pode mais chamar event.preventDefault() desta forma por motivos de desempenho. Você deve definir a opção passive como false no ouvinte de eventos:

 // Prevents window from moving on touch on newer browsers. window.addEventListener('touchmove', function (event) { event.preventDefault() }, {passive: false})

Bibliotecas

Você pode encontrar uma biblioteca chamada “iNoBounce” que foi criada para “impedir que seu aplicativo da web iOS salte ao rolar”. Uma coisa a ser observada ao usar esta biblioteca agora para resolver o problema que descrevi neste artigo é que ela precisa que você use -webkit-overflow-scrolling . Outra coisa a notar é que a solução mais concisa que acabei (que é descrita mais tarde) faz uma coisa semelhante à do iOS. Você pode testar isso olhando os exemplos em seu repositório GitHub e comparando-o com a solução que encontrei.

Comportamento de rolagem

Depois de experimentar todas essas soluções, descobri a propriedade CSS overscroll-behavior . A propriedade CSS overscroll-behavior foi implementada no Chrome 63 em dezembro de 2017 e no Firefox 59 em março de 2018. Essa propriedade, conforme descrito na documentação do MDN, “permite controlar o comportamento de estouro de rolagem do navegador — o que acontece quando o limite de um área de rolagem é alcançada.” Essa foi a solução que acabei usando.

Tudo o que eu tinha que fazer era definir overscroll-behavior como none no body do meu site e eu poderia deixar a position do rodapé como fixed . Embora a rolagem baseada em momento fosse aplicada a toda a página, em vez do conteúdo sem o rodapé, essa solução foi boa o suficiente para mim e atendeu a todos os meus requisitos naquele momento, e meu rodapé não voltou mais inesperadamente no Chrome. Talvez seja útil observar que o Edge tem essa propriedade sinalizada como em desenvolvimento agora. overscroll-behavior pode ser visto como um aprimoramento se os navegadores ainda não o suportarem.

Conclusão

Se você não quiser que seus cabeçalhos ou rodapés fixos saltem em suas páginas da web, agora você pode usar a propriedade CSS overscroll-behavior .

Apesar do fato de que esta solução funciona de forma diferente em diferentes navegadores (o salto do conteúdo da página ainda acontece no Safari e no Edge, enquanto no Firefox e no Chrome não), ela manterá o cabeçalho ou rodapé fixo quando você rolar até o topo ou parte inferior de um site. É uma solução concisa e, em todos os navegadores testados, a rolagem baseada em momento ainda funciona, para que você possa percorrer muito conteúdo da página muito rapidamente. Se você estiver criando um cabeçalho ou rodapé fixo em sua página da Web, poderá começar a usar esta solução.