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

Dave Tapuska
Dave Tapuska

Sabemos que la capacidad de respuesta del desplazamiento es fundamental para la participación del usuario con un sitio web en dispositivos móviles, pero los objetos de escucha de eventos táctiles suelen causar problemas graves de rendimiento del desplazamiento. Chrome abordó este problema permitiendo que los objetos de escucha de eventos táctiles sean pasivos (pasando la opción {passive: true} a addEventListener()) y enviando la API de eventos del puntero. Estas son excelentes funciones para incorporar contenido nuevo en modelos que no bloquean el desplazamiento, pero a veces a los desarrolladores les resulta difícil comprenderlas y adoptarlas.

Creemos que la Web debe ser rápida de forma predeterminada sin que los desarrolladores deban comprender detalles esotéricos del comportamiento del navegador. En Chrome 56, configuramos los objetos de escucha de toques como pasivos de forma predeterminada en los casos en que eso 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 mínimo en los sitios.

En casos excepcionales, este cambio puede provocar un desplazamiento no deseado. Por lo general, esto se resuelve fácilmente aplicando un estilo touch-action: none al elemento en el que no debe ocurrir el desplazamiento. Sigue leyendo para obtener detalles, saber si te afecta y qué puedes hacer al respecto.

En segundo plano: Los eventos cancelables ralentizan tu página

Si llamas a preventDefault() en los eventos touchstart o touchmove, evitarás el desplazamiento. El problema es que, con mayor frecuencia, los objetos de escucha no llamarán a preventDefault(), pero el navegador debe esperar a que finalice el evento para asegurarse de eso. Los "objetos de escucha de eventos pasivos" definidos por el desarrollador resuelven este problema. Cuando agregas un evento de toque con un objeto {passive: true} como tercer parámetro en el controlador de eventos, le indicas al navegador que el objeto de escucha touchstart no llamará a preventDefault() y que el navegador 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 motivación principal es reducir el tiempo que tarda en actualizarse la pantalla después de que el usuario la toca. Para comprender el uso de touchstart y touchmove, agregamos métricas para determinar con qué frecuencia se produjo el comportamiento de bloqueo del desplazamiento.

Analizamos el porcentaje de eventos táctiles anulables 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, ya que estos eventos se vuelven automáticamente "pasivos".

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

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 tardó 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 son para todos los sitios web en Chrome para Android. Antes de que se habilitara la intervención, el 1% de los desplazamientos tardaba poco más de 400 ms. Ahora se redujo a poco más de 250 ms en Chrome 56 beta, una reducción de alrededor del 38%. En el futuro, esperamos que el valor predeterminado de todos los objetos de escucha touchstart y touchmove sea pasivo verdadero, lo que reducirá el tiempo a menos de 50 ms.

Gráfico de los tiempos de desplazamiento del 1% superior

Orientación y fallas

En la gran mayoría de los casos, no se observará ningún daño. Sin embargo, cuando se produce la ruptura, 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 observar eventos de clic inesperados (cuando falta preventDefault() en un objeto de escucha touchend).

En Chrome 56 y versiones posteriores, Herramientas para desarrolladores registrará 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

Tu aplicación puede determinar si puede estar ocurriendo en el entorno real. Para ello, verifica si llamar a preventDefault tuvo algún efecto a través de la propiedad defaultPrevented.

Descubrimos que la gran mayoría de las páginas afectadas se corrigen con relativa facilidad cuando se aplica la propiedad CSS touch-action siempre que sea posible. Si deseas evitar que el navegador realice el desplazamiento y el zoom dentro de un elemento, aplícale touch-action: none. Si tienes un carrusel horizontal, considera aplicarle touch-action: pan-y pinch-zoom para que el usuario pueda desplazarse verticalmente y acercar y alejar como de costumbre. Ya es necesario aplicar correctamente la acción táctil en navegadores como Edge para computadoras de escritorio que admiten eventos de puntero y no eventos táctiles. En el caso de Safari para dispositivos móviles y navegadores para dispositivos móviles más antiguos que no admiten la acción táctil, tus objetos de escucha táctiles deben seguir llamando a preventDefault, incluso cuando Chrome lo ignore.

En casos más complejos, es posible que también debas usar 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 los objetos de escucha de touchend asociados para seguir suprimiendo la generación de eventos de clic y otro comportamiento de presión predeterminado.
  • Como último recurso (y no se recomienda), pasa {passive: false} a addEventListener() para anular el comportamiento predeterminado. Ten en cuenta que deberás detectar funciones si el agente de usuario admite EventListenerOptions.

Conclusión

En Chrome 56, el desplazamiento comienza mucho 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 un desplazamiento no deseado.

Aunque aún es necesario hacerlo para Safari para dispositivos móviles, los sitios web no deben depender de llamar a preventDefault() dentro de los objetos de escucha touchstart y touchmove, ya que ya no se garantiza que se respete en Chrome. Los desarrolladores deben aplicar la propiedad CSS touch-action en los elementos en los que se deben inhabilitar el desplazamiento y el zoom para notificar al navegador antes de que se produzcan eventos táctiles. Para suprimir el comportamiento predeterminado de una presión (como la generación de un evento de clic), llama a preventDefault() dentro de un objeto de escucha touchend.