Sabemos que a capacidade de resposta da rolagem é fundamental para o engajamento do usuário com
um site em dispositivos móveis, mas os listeners de eventos de toque geralmente causam sérios
problemas de desempenho de rolagem. O Chrome está resolvendo esse problema permitindo que listeners de
eventos de toque sejam
passivos
(transmitindo a opção {passive: true}
para addEventListener()
) e enviando a
API de eventos de ponteiro.
Esses são ótimos recursos para direcionar novos conteúdos em modelos que não bloqueiam
a rolagem, mas às vezes os desenvolvedores os consideram difíceis de entender e adotar.
Acreditamos que a Web deve ser rápida por padrão sem que os desenvolvedores precisem entender detalhes misteriosos do comportamento do navegador. No Chrome 56, os listeners de toque são definidos como passivos por padrão nos casos em que isso geralmente corresponde à intenção do desenvolvedor. Acreditamos que, ao fazer isso, podemos melhorar muito a experiência do usuário e, ao mesmo tempo, ter um impacto negativo mínimo nos sites.
Em casos raros, essa mudança pode resultar em rolagem não intencional. Isso geralmente é resolvido com facilidade aplicando um estilo touch-action: nenhum ao elemento em que a rolagem não deve ocorrer. Continue lendo para saber os detalhes, saber se você foi afetado e o que pode fazer a respeito.
Segundo plano: eventos canceláveis deixam sua página mais lenta
Se você chamar
preventDefault()
nos primeiros eventos touchmove
ou touchstart
, evitará a rolagem.
O problema é que, na maioria das vezes, os listeners não chamam preventDefault()
, mas
o navegador precisa aguardar a conclusão do evento para ter certeza.
Os "listeners de eventos passivos" definidos pelo desenvolvedor resolvem essa questão. Ao adicionar um evento de toque com um objeto {passive: true}
como o terceiro parâmetro no manipulador de eventos, você informa ao navegador que o listener touchstart
não chamará preventDefault()
e que o navegador poderá executar a 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 das funções touchstart e touchmove, adicionamos métricas para determinar com que frequência o comportamento de bloqueio de rolagem ocorria.
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 dessa forma. Considerando a dimensão desse problema, notamos uma grande oportunidade de melhorar a rolagem sem nenhuma ação do desenvolvedor, tornando esses eventos "passivos" automaticamente.
Isso nos levou a definir nossa intervenção como: se o destino de um listener touchstart ou
touchmove é o window
, document
ou body
, o padrão é passive
como true
. Isso significa que códigos como:
window.addEventListener("touchstart", func);
se torna 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% de rolagem da parte de cima desde o momento em que um
usuário toca na tela para rolar a tela até o momento em que a tela é atualizada. Esses dados são para todos os sites no Chrome para Android. Antes da intervenção ser ativada,
1% das rolagens levavam 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
a passiva verdadeira como o padrão para todos os listeners touchstart
e touchmove
,
reduzindo esse valor para menos de 50 ms.
Falha e orientação
Na grande maioria dos casos, não serão observadas falhas. No entanto, quando ocorre uma falha, o sintoma mais comum é que a rolagem acontece quando você não quer. Em casos raros, os desenvolvedores também podem notar eventos de click inesperados,
quando preventDefault()
estava 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 aplicativo pode determinar se essa ação pode ser atingida,
verificando se a chamada de preventDefault
teve algum efeito pela
propriedade
defaultPrevented
.
Descobrimos que a maioria das páginas afetadas é corrigida com relativa facilidade, aplicando a propriedade CSS touch-action sempre que possível. Se você quiser impedir toda a rolagem e
o zoom de um elemento no navegador, aplique touch-action: none
a ele. Se você tiver um
carrossel horizontal, considere aplicar touch-action: pan-y pinch-zoom
a ele para que
o usuário ainda possa rolar verticalmente e aplicar zoom normalmente. Já é necessário aplicar
a ação de toque corretamente em navegadores como o Edge para computadores
que oferecem suporte a eventos de ponteiro, não a eventos de toque. No Safari para dispositivos móveis e navegadores
para dispositivos móveis mais antigos que não são compatíveis com a ação por toque, os listeners de toque precisam
continuar chamando preventDefault
mesmo quando ele for ignorado pelo Chrome.
Em casos mais complexos, pode ser necessário recorrer a uma das opções a seguir:
- Se o listener
touchstart
chamarpreventDefault()
, faça com que preventDefault() também seja chamado nos listeners de touchend associados para continuar suprimindo a geração de eventos de clique e outros comportamentos de toque padrão. - Por último (e não recomendado), transmita
{passive: false}
para addEventListener() a fim de substituir o comportamento padrão. Você precisará detectar se o user agent é compatível com EventListenerOptions.
Conclusão
No Chrome 56, a rolagem começa substancialmente mais rápido em muitos sites. Esse é o único impacto que a maioria dos desenvolvedores 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 podem
confiar em chamar preventDefault()
dentro dos listeners touchstart
e touchmove
,
já que não há mais garantias de que isso será mantido no Chrome. Os desenvolvedores
precisam aplicar a propriedade CSS touch-action
nos elementos em que a rolagem e
o zoom precisam ser desativados para notificar o navegador antes que ocorra qualquer evento de toque.
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
.