Cómo acelerar el desplazamiento táctil de manera predeterminada

Dave Tapuska
Dave Tapuska

Sabemos que la capacidad de respuesta durante el desplazamiento es fundamental para la participación del usuario en un sitio web en un dispositivo móvil. Sin embargo, los objetos de escucha de eventos táctiles suelen causar problemas graves de rendimiento de desplazamiento. Para solucionar este problema, Chrome permite que los objetos de escucha de eventos táctiles sean pasivos (pasando la opción {passive: true} a addEventListener()) y envía la API de eventos de puntero. Estas son funciones excelentes para incluir contenido nuevo en modelos que no bloquean el desplazamiento, pero a veces los desarrolladores tienen dificultades de entender y adoptar.

Creemos que la Web debería ser rápida de forma predeterminada sin que los desarrolladores necesiten comprender detalles superficiales del comportamiento de los navegadores. En Chrome 56, de forma predeterminada, cambiamos los objetos de escucha táctiles a pasivos en los casos en que más a menudo coincida con la intención del desarrollador. Creemos que, de esta manera, podemos mejorar en gran medida la experiencia del usuario y, al mismo tiempo, tener un impacto negativo en los sitios mínimo.

En casos excepcionales, este cambio puede provocar un desplazamiento no deseado. Por lo general, esto se soluciona con facilidad mediante la aplicación de un estilo touch-action: none en el elemento en el que no debe ocurrir el desplazamiento. Sigue leyendo para obtener más detalles, descubrir cómo saber si te afecta este cambio y qué puedes hacer al respecto.

Fondo: Los eventos cancelables ralentizan tu página

Si llamas a preventDefault() en los eventos touchstart o primeros touchmove, evitarás el desplazamiento. El problema es que, en la mayoría de los casos, los objetos de escucha no llamarán a preventDefault(), pero el navegador debe esperar a que finalice el evento para asegurarse de ello. Los "objetos de escucha de eventos pasivos" definidos por el desarrollador solucionan esto. Cuando agregas un evento táctil con un objeto {passive: true} como tercer parámetro en tu controlador de eventos, le indicas al navegador que el objeto de escucha de touchstart no llamará a preventDefault() y que este puede realizar el desplazamiento de forma segura sin bloquear el objeto de escucha. Por ejemplo:

window.addEventListener("touchstart", func, {passive: true} );

La intervención

Nuestra principal motivación es reducir el tiempo que lleva actualizar la pantalla después de que el usuario la toca. Para comprender el uso de touchstart y touchmove, agregamos métricas que permiten determinar la frecuencia con la que se bloquea el desplazamiento.

Observamos el porcentaje de eventos táctiles cancelables que se enviaron a un objetivo raíz (ventana, documento o cuerpo) y determinamos que alrededor del 80% de estos objetos de escucha son conceptualmente pasivos, pero no se registraron como tales. Dada la escala de este problema, notamos una gran oportunidad para mejorar el desplazamiento sin ninguna acción del desarrollador haciendo que estos eventos sean "pasivos" automáticamente.

Esto nos llevó a definir nuestra intervención de la siguiente manera: si el objetivo de un objeto de escucha touchstart o touchmove es window, document o body, usamos passive de forma predeterminada en true. Esto significa que códigos como los siguientes:

window.addEventListener("touchstart", func);

se convierte en lo siguiente:

window.addEventListener("touchstart", func, {passive: true} );

Ahora se ignorarán las llamadas a preventDefault() dentro del objeto de escucha.

En el siguiente gráfico, se muestra el tiempo que tarda el 1% superior de los desplazamientos desde el momento en que un usuario toca la pantalla para desplazarse hasta el momento en que se actualiza la pantalla. Estos datos corresponden a todos los sitios web en Chrome para Android. Antes de que se habilitara la intervención, el 1% de los desplazamientos tardaba un poco más de 400 ms. Eso se redujo a un poco más de 250 ms en Chrome 56 Beta (una reducción de aproximadamente el 38%). En el futuro, esperamos que la configuración pasiva sea el valor predeterminado para todos los objetos de escucha touchstart y touchmove, lo que reducirá esta cantidad a menos de 50 ms.

Gráfico de tiempos de sroll del 1% superior

Falla y orientación

En la gran mayoría de los casos, no se observarán fallas. Sin embargo, cuando se produce una falla, el síntoma más común es que el desplazamiento se produce cuando no lo deseas. En casos excepcionales, los desarrolladores también pueden notar eventos de clic inesperados (cuando faltaba preventDefault() en un objeto de escucha touchend).

En Chrome 56 y versiones posteriores, las Herramientas para desarrolladores registrarán una advertencia cuando llames a preventDefault() en un evento en el que la intervención esté activa.

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

Para determinar si la aplicación está en ese estado, verifica si la llamada a preventDefault tuvo algún efecto a través de la propiedad defaultPrevented.

Descubrimos que una gran mayoría de las páginas afectadas se corrigen con bastante facilidad mediante la aplicación de la propiedad de CSS de acción táctil siempre que sea posible. Si deseas evitar todo el desplazamiento y el zoom del navegador dentro de un elemento, aplica touch-action: none en él. Si tienes un carrusel horizontal, considera aplicar touch-action: pan-y pinch-zoom para que el usuario pueda desplazarse verticalmente y hacer zoom con normalidad. Ya es necesario aplicar la acción táctil de forma correcta en navegadores como los de Edge de escritorio, que admiten eventos de puntero y no eventos táctiles. En el caso de Safari para dispositivos móviles y los navegadores para dispositivos móviles anteriores que no admitan acciones táctiles, tus objetos de escucha táctiles deberán seguir llamando a preventDefault, incluso si Chrome lo ignorará.

En casos más complejos, también puede ser necesario basarse en una de las siguientes opciones:

  • Si tu objeto de escucha touchstart llama a preventDefault(), asegúrate de que también se llame a preventDefault() desde objetos de escucha de touchend asociados para seguir suprimiendo la generación de eventos de clic y otro comportamiento de toque predeterminado.
  • Por último (y no se recomienda), pasa {passive: false} a addEventListener() para anular el comportamiento predeterminado. Ten en cuenta que deberás detectar características si el usuario-agente admite EventListenerOptions.

Conclusión

En Chrome 56, el desplazamiento comienza considerablemente más rápido en muchos sitios web. Este es el único impacto que la mayoría de los desarrolladores notarán como resultado de este cambio. En algunos casos, los desarrolladores pueden notar desplazamientos no intencionados.

Si bien sigue siendo necesario hacerlo en el caso de Safari para dispositivos móviles, los sitios web no deberían depender de llamar a preventDefault() dentro de los objetos de escucha touchstart y touchmove, ya que no se garantiza que esto se cumpla en Chrome. Los desarrolladores deben aplicar la propiedad touch-action de CSS en los elementos en los que el desplazamiento y el zoom deben estar inhabilitados para notificar al navegador antes de que ocurra cualquier evento táctil. Para suprimir el comportamiento predeterminado de un toque (como la generación de un evento de clic), llama a preventDefault() dentro de un objeto de escucha touchend.