Prenez le contrôle de votre défilement en personnalisant les effets d'actualisation et de dépassement.

Eric Bidelman
Majid Valipour

Résumé

La propriété CSS overscroll-behavior permet aux développeurs d'ignorer le comportement de défilement par défaut du navigateur lorsqu'il atteint le haut/bas du contenu. Il peut s'agir de la désactivation de la fonctionnalité PullRefresh sur mobile, de la suppression du halo de défilement hors limites et des effets d'élastique, et d'empêcher le contenu de la page de faire défiler lorsqu'elle se trouve sous une fenêtre modale ou en superposition.

Contexte

Limites et chaînage de défilement

Enchaînement de défilements sur Chrome pour Android

Le défilement est l'un des moyens les plus fondamentaux d'interagir avec une page, mais certains modèles d'expérience utilisateur peuvent être difficiles à gérer en raison des comportements par défaut décalés du navigateur. Prenons l'exemple d'un panneau d'applications contenant un grand nombre d'éléments que l'utilisateur devra peut-être faire défiler. Lorsqu'ils atteignent le bas de l'écran, le conteneur de dépassement de capacité arrête le défilement, car il n'y a plus de contenu à consommer. En d'autres termes, l'utilisateur atteint une "limite de défilement". Notez ce qui se passe si l'utilisateur continue de faire défiler la page. Le contenu derrière le panneau commence à défiler. Le défilement est repris par le conteneur parent (la page principale elle-même dans l'exemple).

Il s'avère que ce comportement est appelé enchaînement de défilement, c'est-à-dire le comportement par défaut du navigateur lors du défilement du contenu. La valeur par défaut est souvent assez intéressante, mais elle n'est parfois ni souhaitable, ni même inattendue. Certaines applications peuvent souhaiter offrir une expérience utilisateur différente lorsque l'utilisateur atteint une limite de défilement.

Effet PullRefresh

Pull-to-Refresh est un geste intuitif popularisé par les applications mobiles telles que Facebook et Twitter. Tirer sur un flux de réseau social et le publier permettent de créer un espace pour le chargement des posts plus récents. En fait, cette expérience utilisateur particulière est devenue si populaire que les navigateurs mobiles tels que Chrome sur Android ont adopté le même effet. En faisant glisser le doigt vers le bas en haut de la page, vous actualisez toute la page:

Fonctionnalité pull-to-refresh personnalisée de Twitter
lors de l'actualisation d'un flux dans sa PWA.
L'action d'actualisation native de Chrome pour Android
actualise la page entière.

Pour des situations telles que la PWA Twitter, il peut être judicieux de désactiver l'action native PullRefresh. Pourquoi ? Dans cette application, vous ne souhaitez probablement pas que l'utilisateur actualise accidentellement la page. Il est également possible de voir une animation de double actualisation. Il peut également être utile de personnaliser l'action du navigateur, en l'alignant plus précisément sur le branding du site. Malheureusement, ce type de personnalisation a été difficile à mettre en œuvre. Les développeurs finissent par écrire du code JavaScript inutile, ajoutent des écouteurs tactiles non passifs (qui bloquent le défilement) ou maintiennent la page entière dans un <div> 100 vw/vh (pour éviter le dépassement de la page). Ces solutions ont des effets négatifs bien documentés sur les performances de défilement.

On peut faire mieux !

Découvrez overscroll-behavior

La propriété overscroll-behavior est une nouvelle fonctionnalité CSS qui contrôle le comportement de ce qui se passe lorsque vous faites défiler un conteneur de manière prolongée (y compris la page elle-même). Vous pouvez l'utiliser pour annuler les chaînages de défilement, désactiver/personnaliser l'action d'actualisation, désactiver les effets de bandes sur iOS (lorsque Safari implémente overscroll-behavior), et plus encore. L'avantage, c'est que l'utilisation de overscroll-behavior n'affecte pas les performances de la page, comme les astuces mentionnées dans l'introduction.

Trois valeurs sont possibles pour la propriété:

  1. auto : valeur par défaut. Les défilements provenant de l'élément peuvent se propager aux éléments ancêtres.
  2. contain : empêche les chaînes de défilement. Les défilements ne se propagent pas aux ancêtres, mais les effets locaux dans le nœud sont affichés. Il peut s'agir, par exemple, de l'effet de halo de défilement hors limites sur Android ou de l'effet d'étanchéité sur iOS, qui avertit l'utilisateur lorsqu'il atteint une limite de défilement. Remarque: L'utilisation de overscroll-behavior: contain sur l'élément html empêche le défilement hors limites des actions de navigation.
  3. none : identique à contain, mais empêche également les effets de défilement hors limites dans le nœud lui-même (par exemple, le défilement hors limites Android ou l'altération iOS).

Examinons quelques exemples pour comprendre comment utiliser overscroll-behavior.

Empêcher les défilements d'échapper à un élément fixe

Le scénario de chatbox

Le contenu situé sous la fenêtre de chat défile également :(

Prenons l'exemple d'une fenêtre de chat fixe en bas de la page. L'objectif est que le chatbox soit un composant autonome et qu'il puisse défiler séparément du contenu qui se trouve derrière. Toutefois, en raison d'un enchaînement de défilement, le document commence à défiler dès que l'utilisateur clique sur le dernier message de l'historique des discussions.

Pour cette application, il est plus approprié que les défilements provenant de la fenêtre de chat restent dans le chat. Pour ce faire, nous pouvons ajouter overscroll-behavior: contain à l'élément qui contient les messages de chat:

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

En gros, nous créons une séparation logique entre le contexte de défilement du chatbox et la page principale. Au final, la page principale reste disponible lorsque l'utilisateur atteint le haut/bas de l'historique des discussions. Les défilements qui commencent dans la fenêtre de chat ne se propagent pas.

Scénario d'une superposition de page

Une autre variante du scénario de défilement "underscroll" est la suivante : le contenu défile derrière une superposition de position fixe. overscroll-behavior, c'est de l'ordre ! Le navigateur essaie d'être utile, mais il finit par donner au site l'air buggé.

Exemple - modal avec et sans overscroll-behavior: contain:

Avant: le contenu de la page défile sous la superposition.
Après: le contenu de la page ne défile pas sous la superposition.

Désactiver la fonctionnalité PullRefresh

La désactivation de l'action d'actualisation est une simple ligne de code CSS. Empêchez simplement le défilement de l'enchaînement sur l'ensemble de l'élément qui définit la fenêtre d'affichage. Dans la plupart des cas, il s'agit de <html> ou <body>:

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

Grâce à cet ajout simple, nous corrigeons les doubles animations d'actualisation en mode Pull dans la démonstration du chatbox et nous pouvons à la place implémenter un effet personnalisé qui utilise une animation de chargement plus soignée. L'ensemble de la boîte de réception est également flouté à mesure qu'elle s'actualise:

Avant
Après

Voici un extrait du code complet:

<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>

Désactiver les effets de halo de défilement hors limites et d'étanchéité

Pour désactiver l'effet de rebond lorsque vous atteignez une limite de défilement, utilisez overscroll-behavior-y: none:

body {
  /* Disables pull-to-refresh and overscroll glow effect.
     Still keeps swipe navigations. */
  overscroll-behavior-y: none;
}
Avant: appuyer sur la limite de défilement affiche un halo.
Après: halo désactivé.

Démo complète

En résumé, la démonstration complète de la fenêtre de chat utilise overscroll-behavior pour créer une animation pull-to-refresh personnalisée et empêcher les défilements d'échapper le widget de chatbox. Vous bénéficiez ainsi d'une expérience utilisateur optimale qui aurait été difficile à obtenir sans CSS overscroll-behavior.

Voir la démonstration | Source