Sabemos que a capacidade de rolagem é essencial para o engajamento do usuário com
um site em dispositivos móveis. No entanto, os listeners de eventos de toque geralmente causam problemas graves de
desempenho de rolagem. O Chrome está resolvendo esse problema permitindo que os listeners de eventos de toque
sejam
passivos
(transmitindo a opção {passive: true}
para addEventListener()
) e enviando a
API eventos do ponteiro.
Esses são ótimos recursos para direcionar novos conteúdos a modelos que não bloqueiam
a rolagem, mas os desenvolvedores às vezes têm dificuldade para entender e adotar esses recursos.
Acreditamos que a Web precisa ser rápida por padrão sem que os desenvolvedores precisem entender detalhes obscuros do comportamento do navegador. No Chrome 56, estamos definindo o padrão de listeners de toque como passivos nos casos em que isso corresponde à intenção do desenvolvedor. Acreditamos que, ao fazer isso, podemos melhorar muito a experiência do usuário, com impacto mínimo negativo nos sites.
Em casos raros, essa mudança pode resultar em rolagem indesejada. Isso geralmente é fácil de resolver aplicando um estilo touch-action: none ao elemento em que a rolagem não deve ocorrer. Leia mais para saber detalhes, como saber se você foi afetado e o que fazer a respeito.
Contexto: os eventos canceláveis diminuem a velocidade da sua página
Se você chamar
preventDefault()
nos eventos touchstart
ou touchmove
, a rolagem será impedida.
O problema é que, na maioria das vezes, os listeners não chamam preventDefault()
, mas
o navegador precisa esperar o evento terminar para ter certeza disso.
Os "listeners de eventos passivos" definidos pelo desenvolvedor resolvem esse problema. Quando você adiciona um evento
de toque com um objeto {passive: true}
como o terceiro parâmetro no gerenciador
de eventos, informa ao navegador que o listener touchstart
não vai
chamar preventDefault()
e que o navegador pode executar o rolagem com segurança sem
bloquear o listener. Exemplo:
window.addEventListener("touchstart", func, {passive: true} );
A intervenção
Nossa principal motivação é reduzir o tempo necessário para atualizar a tela depois que o usuário toca nela. Para entender o uso de touchstart e touchmove, adicionamos métricas para determinar a frequência com que o comportamento de bloqueio de rolagem ocorreu.
Analisamos a porcentagem de eventos de toque canceláveis enviados para um destino raiz (janela, documento ou corpo) e determinamos que cerca de 80% desses listeners são conceitualmente passivos, mas não foram registrados como tal. Dada a escala desse problema, notamos uma grande oportunidade de melhorar a rolagem sem nenhuma ação do desenvolvedor, tornando esses eventos automaticamente "passivos".
Isso nos levou a definir nossa intervenção como: se o alvo de um listener de touchstart ou
touchmove for window
, document
ou body
, vamos definir
passive
como true
por padrão. Isso significa que um código como:
window.addEventListener("touchstart", func);
fica equivalente a:
window.addEventListener("touchstart", func, {passive: true} );
Agora, as chamadas para preventDefault()
dentro do listener serão ignoradas.
O gráfico abaixo mostra o tempo gasto pelo 1% superior de rolagens desde o momento em que o
usuário toca na tela para rolar até o momento em que a tela é atualizada. Esses dados
são de todos os sites no Chrome para Android. Antes da intervenção ser ativada,
1% dos rolamentos levava pouco mais de 400 ms. Isso foi reduzido para pouco mais de 250 ms
no Chrome 56 Beta, uma redução de cerca de 38%. No futuro, esperamos tornar
o padrão para todos os listeners touchstart
e touchmove
verdadeiro, reduzindo-o para menos de 50 ms.

Interrupção e orientação
Na grande maioria dos casos, não há quebra. No entanto, quando ocorre
um erro, o sintoma mais comum é que a rolagem acontece quando você não
quer. Em casos raros, os desenvolvedores também podem notar eventos de clique inesperados
(quando o preventDefault()
está ausente em um listener touchend
).
No Chrome 56 e versões mais recentes, o DevTools registra um aviso quando você chama
preventDefault()
em um evento em que a intervenção está ativa.
touch-passive.html:19 Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080
O app pode determinar se ele pode estar atingindo esse limite
verificando se a chamada de preventDefault
teve algum efeito na
propriedade
defaultPrevented
.
Descobrimos que a grande maioria das páginas afetadas pode ser corrigida com relativa facilidade
aplicando a propriedade
touch-action
do CSS sempre que possível. Se você quiser impedir a rolagem e
o zoom do navegador em um elemento, aplique touch-action: none
a ele. Se você tiver um
carrossel horizontal, aplique touch-action: pan-y pinch-zoom
a ele para
que o usuário ainda possa rolar verticalmente e fazer zoom normalmente. Aplicar
a ação de toque corretamente já é necessário em navegadores como o Edge para computador
que oferecem suporte a eventos de ponteiro, mas não a eventos de toque. Para o Safari para dispositivos móveis e navegadores mais antigos
que não oferecem suporte a ações de toque, seus listeners de toque precisam
continuar chamando preventDefault
, mesmo que ele seja ignorado pelo Chrome.
Em casos mais complexos, talvez seja necessário usar uma das seguintes opções:
- Se o listener
touchstart
chamarpreventDefault()
, verifique se preventDefault() também é chamado pelos listeners de touchend associados para continuar suprimindo a geração de eventos de clique e outros comportamentos de toque padrão. - A última (e desencorajada) transmissão
{passive: false}
para addEventListener() para substituir o comportamento padrão. É necessário detectar se o User Agent oferece suporte a EventListenerOptions.
Conclusão
No Chrome 56, a rolagem começa muito mais rápido em muitos sites. Esse é o único impacto que a maioria dos desenvolvedores vai notar como resultado dessa mudança. Em alguns casos, os desenvolvedores podem notar rolagem não intencional.
Embora ainda seja necessário fazer isso no Safari para dispositivos móveis, os sites não devem
depender de chamadas de preventDefault()
dentro de listeners touchstart
e touchmove
,
já que isso não é mais garantido no Chrome. Os desenvolvedores
precisam aplicar a propriedade CSS touch-action
em elementos em que a rolagem e
o zoom precisam ser desativados para notificar o navegador antes que qualquer evento de toque ocorra.
Para suprimir o comportamento padrão de um toque (como a geração de um evento
de clique), chame preventDefault()
dentro de um listener touchend
.