Controla tu desplazamiento: personaliza los efectos de "deslizar hacia abajo para actualizar" y "desbordamiento",

A modo de resumen

La propiedad overscroll-behavior de CSS permite a los desarrolladores anular el comportamiento de desplazamiento de desbordamiento predeterminado del navegador cuando se llega a la parte superior o inferior del contenido. Los casos de uso incluyen inhabilitar la función de deslizar para actualizar en dispositivos móviles, quitar el brillo de desplazamiento excesivo y los efectos de goma elástica, y evitar que el contenido de la página se desplace cuando está debajo de un elemento modal o superpuesto.

Segundo plano

Límites de desplazamiento y encadenamiento de desplazamientos

Cadena de desplazamiento en Chrome para Android.

El desplazamiento es una de las formas más fundamentales de interactuar con una página, pero puede ser difícil lidiar con ciertos patrones de UX debido a la peculiar los comportamientos predeterminados. Como ejemplo, toma un panel lateral de apps con una gran cantidad de elementos que el usuario podría tener que desplazarse. Cuando llegan al fondo, el contenedor de desbordamiento detiene el desplazamiento porque no hay más contenido para consumir. En otras palabras, el usuario alcanza un "límite de desplazamiento". Pero observa lo que sucede si el usuario sigue desplazar. El contenido detrás del panel lateral comienza a desplazarse. El desplazamiento es tomada por el contenedor superior; la página principal en el ejemplo.

Resulta que este comportamiento se denomina encadenamiento de desplazamiento. la configuración predeterminada comportamiento cuando te desplazas por el contenido. A menudo, el valor predeterminado es bastante bueno, pero a veces no es deseable ni siquiera inesperado. Es posible que algunas apps quieran proporcionar una experiencia del usuario diferente cuando alcanza un límite de desplazamiento.

Efecto tirar para actualizar

"Arrastrar para actualizar" es un gesto intuitivo popularizado por aplicaciones para dispositivos móviles como Facebook y Twitter. Si acercas la atención a un feed social y la lanzas, se crean espacio para que se carguen las publicaciones más recientes. De hecho, esta UX en particular tiene se han vuelto tan populares que los navegadores para dispositivos móviles, como Chrome en Android, han adoptado el mismo efecto. Si deslizas el dedo hacia abajo en la parte superior de la página, se actualizará toda la página:

El gesto de deslizar hacia abajo para actualizar de Twitter
cuando se actualiza un feed en su AWP.
La acción nativa de deslizar para actualizar de Chrome para Android
actualiza todo .

En situaciones como la AWP de Twitter, podría tener sentido inhabilitar la acción nativa de deslizar para actualizar. ¿Por qué? En esta app, es probable que no quieras que el usuario actualice la página por accidente. También es posible ver una animación de actualización doble. Como alternativa, podría ser mejor personalizar la acción del navegador y alinearla más con el desarrollo de la marca del sitio. Lo lamentable es que este tipo de personalización es difícil de realizar. Los desarrolladores terminan escribiendo JavaScript innecesario, agregan objetos de escucha de toque no pasivos (que bloquean el desplazamiento) o pegan toda la página en un <div> de 100 vw/vh (para evitar que la página se desborde). Estas soluciones alternativas tienen negativa bien documentada en el rendimiento del desplazamiento.

¡Podemos hacerlo mejor!

Presentamos overscroll-behavior

La propiedad overscroll-behavior es una nueva función de CSS que controla el comportamiento de lo que sucede cuando te desplazas sobre un contenedor (incluida la esta página). Puedes usarlo para cancelar el encadenamiento de desplazamiento, inhabilitar o personalizar acción de deslizar para actualizar, inhabilitar los efectos de banda elástica en iOS (cuando Safari implementa overscroll-behavior) y mucho más. Lo mejor es que usar overscroll-behavior no afecta negativamente el rendimiento de la página, como los hacks mencionados en la introducción.

La propiedad puede tomar tres valores posibles:

  1. auto: Predeterminado. Los desplazamientos que se originan en el elemento pueden propagarse a los elementos superiores.
  2. contain: Evita el encadenamiento de desplazamiento. Los desplazamientos no se propagan a los ancestros, pero se muestran los efectos locales dentro del nodo. Por ejemplo, el efecto de brillo de desplazamiento excesivo en Android o el efecto de goma elástica en iOS, que notifica al usuario cuando alcanza un límite de desplazamiento. Nota: El uso de overscroll-behavior: contain en el elemento html evita las acciones de navegación de desplazamiento excesivo.
  3. none: Es igual que contain, pero también evita los efectos de desplazamiento dentro del elemento. el nodo en sí (p.ej., brillo del sobredesplazamiento de Android o banda elástica de iOS).

Analicemos algunos ejemplos para ver cómo usar overscroll-behavior.

Evita que los desplazamientos escapen de un elemento de posición fija

Situación del cuadro de chat

El contenido que aparece debajo de la ventana de chat también se desplaza :(

Considera un cuadro de chat con posición fija que se ubique en la parte inferior de la página. El la intención es que el cuadro de chat sea un componente independiente y que se desplace independiente del contenido detrás de ella. Sin embargo, debido a la encadenación del desplazamiento, el documento comienza a desplazarse en cuanto el usuario presiona el último mensaje en el historial de chat.

Para esta app, es más apropiado que los desplazamientos que se originan dentro del cuadro de chat permanezcan dentro del chat. Para hacerlo, agregaemos overscroll-behavior: contain al elemento que contiene los mensajes de chat:

#chat .msgs {
  overflow: auto;
  overscroll-behavior: contain;
  height: 300px;
}

Básicamente, creamos una separación lógica entre el desplazamiento del cuadro de chat el contexto y la página principal. El resultado final es que la página principal permanece en su lugar cuando el usuario llega a la parte superior o inferior del historial de chat. Los desplazamientos que comienzan en la la ventana de chat no se propagan.

Situación de superposición de la página

Otra variación de la situación de "desplazamiento inferior" es cuando ves contenido que se desplaza detrás de una superposición de posición fija. Un sorteo sin fin overscroll-behavior está en orden. El navegador intenta ser útil, pero termina haciendo que el sitio parezca tener errores.

Ejemplo: Ventana modal con y sin overscroll-behavior: contain:

Antes: El contenido de la página se desplaza debajo de la superposición.
Después: El contenido de la página no se desplaza debajo de la superposición.

Cómo inhabilitar el gesto de deslizar para actualizar

Solo se puede desactivar la acción de deslizar para actualizar con una sola línea de CSS. Solo prevenir el encadenamiento de desplazamiento en todo el elemento que define el viewport. En la mayoría de los casos, es <html> o <body>:

body {
  /* Disables pull-to-refresh but allows overscroll glow effects. */
  overscroll-behavior-y: contain;
}

Con esta simple adición, corregimos las animaciones de doble deslizamiento para actualizar en la demo de cuadro de chat y, en su lugar, podemos implementar un efecto personalizado que usa una animación de carga más ordenada. La bandeja de entrada completa también se desenfoca a medida que se actualiza:

Antes
Después

Este es un fragmento del código completo:

<style>
  body.refreshing #inbox {
    filter: blur(1px);
    touch-action: none; /* prevent scrolling */
  }
  body.refreshing .refresher {
    transform: translate3d(0,150%,0) scale(1);
    z-index: 1;
  }
  .refresher {
    --refresh-width: 55px;
    pointer-events: none;
    width: var(--refresh-width);
    height: var(--refresh-width);
    border-radius: 50%;
    position: absolute;
    transition: all 300ms cubic-bezier(0,0,0.2,1);
    will-change: transform, opacity;
    ...
  }
</style>

<div class="refresher">
  <div class="loading-bar"></div>
  <div class="loading-bar"></div>
  <div class="loading-bar"></div>
  <div class="loading-bar"></div>
</div>

<section id="inbox"><!-- msgs --></section>

<script>
  let _startY;
  const inbox = document.querySelector('#inbox');

  inbox.addEventListener('touchstart', e => {
    _startY = e.touches[0].pageY;
  }, {passive: true});

  inbox.addEventListener('touchmove', e => {
    const y = e.touches[0].pageY;
    // Activate custom pull-to-refresh effects when at the top of the container
    // and user is scrolling up.
    if (document.scrollingElement.scrollTop === 0 && y > _startY &&
        !document.body.classList.contains('refreshing')) {
      // refresh inbox.
    }
  }, {passive: true});
</script>

Cómo inhabilitar los efectos de resplandor y banda elástica de sobredesplazamiento

Para inhabilitar el efecto de rebote cuando se alcanza un límite de desplazamiento, usa overscroll-behavior-y: none:

body {
  /* Disables pull-to-refresh and overscroll glow effect.
     Still keeps swipe navigations. */
  overscroll-behavior-y: none;
}
Antes: Cuando se alcanza el límite de desplazamiento, se muestra un brillo.
Después: Se inhabilitó el brillo.

Demostración completa

Cuando se junta todo, chatbox de demostración, overscroll-behavior para crear una animación personalizada de "deslizar hacia abajo para actualizar" e inhabilita los desplazamientos para evitar que se escapen del widget del cuadro de chat. Esto proporciona una experiencia del usuario óptima que habría sido difícil lograr sin CSS overscroll-behavior.

Ver demostración | Fuente