Éléments tenant compte de la position de défilement

Présentation

Améliorez l'expérience utilisateur des boutons flottants, comme un lien "Retour en haut", en ne les affichant que lorsqu'ils sont utiles. Ce guide explique comment créer ces éléments à l'aide de la propriété CSS container-scroll-state-queries, qui permet de styliser les éléments en fonction de la position de défilement de leur conteneur sans s'appuyer sur des écouteurs ou des observateurs de défilement JavaScript.

Implémentation

1. Établir le conteneur de défilement

Le conteneur de défilement doit être déclaré en tant que conteneur de requête d'état de défilement.

.scroller {
  overflow-y: auto;
  /* Establish this element as a scroll-state query container */
  container-type: scroll-state;
}

2. Styliser l'élément flottant

Placez l'élément dans le conteneur et appliquez-lui un style. Par défaut, il doit être masqué.

.back-to-top {
  position: sticky;
  bottom: 20px;
  visibility: hidden;
  opacity: 0;
  translate: 0 20px;
  transition:
    visibility 0.3s,
    opacity 0.3s ease,
    translate 0.3s ease;
}

3. Répondre à l'état de défilement

Utilisez la règle @container avec la fonction scroll-state. Pour vérifier si l'utilisateur a fait défiler l'écran vers le bas, vérifiez si le conteneur peut défiler vers le haut.

/* When the container can be scrolled toward the top, it means the user has scrolled down */
@container scroll-state(scrollable: top) {
  .back-to-top {
    visibility: visible;
    opacity: 1;
    translate: 0 0;
  }
}

Stratégies de remplacement

Remplacement de base

Si container-scroll-state-queries n'est pas compatible, l'élément flottant reste invisible en raison de la valeur par défaut visibility: hidden. Pour garantir le bon fonctionnement, vous pouvez choisir de rendre l'élément toujours visible dans les navigateurs non compatibles.

/* Fallback for browsers that do not support the feature */
.back-to-top {
  visibility: visible; /* Always visible */
  opacity: 1;
}

/* Override for supported browsers to handle dynamic visibility */
@supports (container-type: scroll-state) {
  .back-to-top {
    visibility: hidden;
    opacity: 0;
  }
  
  @container scroll-state(scrollable: top) {
    .back-to-top {
      visibility: visible;
      opacity: 1;
      translate: 0 0;
    }
  }
}

Solution de remplacement avancée (Intersection Observer)

Si la visibilité dynamique est requise, utilisez un IntersectionObserver pour activer/désactiver une classe lorsqu'un élément sentinelle en haut du défilement disparaît de l'écran.

<!-- Sentinel element placed at the top of the scroller -->
<div class="scroll-sentinel"></div>
/* Marker styling to ensure it does not affect layout */
.scroll-sentinel {
  height: 0;
  width: 0;
  visibility: hidden;
}

.scrolled .back-to-top {
  visibility: visible;
  opacity: 1;
  translate: 0 0;
}
if (!CSS.supports('container-type', 'scroll-state')) {
  const sentinel = document.querySelector('.scroll-sentinel');
  const scroller = document.querySelector('.scroller');

  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      // If the sentinel is NOT intersecting, it means the user has scrolled down
      if (!entry.isIntersecting) {
        scroller.classList.add('scrolled');
      } else {
        scroller.classList.remove('scrolled');
      }
    });
  }, { root: scroller });

  observer.observe(sentinel);
}