Sincronizzazione dello stato di allineamento dello scorrimento

La sincronizzazione dello stato della UI con la posizione di aggancio di un container scorrevole richiedeva tradizionalmente listener di eventi di scorrimento complessi, calcoli manuali degli offset di scorrimento e osservatori di intersezione. L'evento scrollsnapchange fornisce un modo nativo ed efficiente per rilevare quando uno scroller si è stabilizzato su un nuovo target di aggancio, il che lo rende utile per sincronizzare le barre laterali o evidenziare la sezione attiva in un sommario.

Implementazione

1. Configura l'allineamento dello scorrimento in CSS

Il container deve avere scroll-snap-type definito e avere elementi secondari con scroll-snap-align affinché il browser possa monitorare i target di aggancio. In un articolo lungo con un sommario, puoi utilizzare questo attributo per agganciare le intestazioni delle sezioni alla parte superiore dell'area visibile.

main {
    /* Enable scroll snapping on the container */  
  scroll-snap-type: y proximity;
  overflow-y: auto;
}

h2 {
  /* Define how headers align when snapped */
  scroll-snap-align: start;
}

2. Ascolta le modifiche di Snap

Utilizza l'evento scrollsnapchange sul contenitore di scorrimento per reagire quando l'utente termina lo scorrimento e il browser si aggancia a un nuovo elemento. Nella nostra demo del sommario, utilizziamo questa funzionalità per evidenziare il link attivo nella barra laterale.

<!-- MANDATORY: Wrap table of contents links inside a proper navigation landmark -->
<nav aria-label="Table of contents">
  <ul>
    <li><a href="#section-1" aria-current="location">Section 1</a></li>
    <li><a href="#section-2">Section 2</a></li>
  </ul>
</nav>
const main = document.getElementById('main');
const links = document.querySelectorAll('nav a');

// The event fires when the scroller settles on a new snap target
main.addEventListener('scrollsnapchange', (event) => {
  // Use snapTargetBlock for vertical or snapTargetInline for horizontal
  const snappedHeader = event.snapTargetBlock;
  
  if (snappedHeader) {
    setSelectedParagraph(snappedHeader.id);
  }
});

Accessibilità

Per un sommario, assicurati che i link della barra laterale utilizzino aria-current="true" o aria-current="location" quando sono attivi.

Inoltre, fai attenzione quando utilizzi il valore mandatory per scroll-snap-type, in quanto può rendere inaccessibili i contenuti tra i punti di aggancio quando sono più lunghi dello schermo.

Strategie di riserva

Se scrollsnapchange non è supportato, utilizza IntersectionObserver per rilevare quale elemento si trova attualmente nella parte superiore dello scroller. Tieni presente che questo comportamento è diverso da scrollsnapchange, in quanto l'attivazione avviene durante lo scorrimento, anziché solo quando lo scorrimento è terminato.

// Feature detect support for scroll snap events
if (!('onscrollsnapchange' in HTMLElement.prototype)) {
  const observer = new IntersectionObserver(
    () => {
      // Each time the set of intersecting headers changes, find the top
      // header that is visible.
      const topEntry = [...headers].reduce((currentTop, header) => {
        // Use the bottom to handle scrolling up, when the top is still offscreen
        const {bottom} = header.getBoundingClientRect();
        // Don't match if the header's bottom is above the scrollport
        if (bottom < 0) return;
        if (!currentTop) return header;
        return bottom <
          currentTop.getBoundingClientRect().bottom
          ? header
          : currentTop;
      }, undefined);
      if (topEntry) setSelectedParagraph(topEntry.id);
    },
    { root: main, threshold: 0.9 // Adjust based on your use case },
  );

  // Observe all snap targets (e.g., section headers)
  document.querySelectorAll('h2').forEach(header => observer.observe(header));
}