Scroll Snap State Sync

Voor de synchronisatie van de UI-status met de uitlijnpositie van een scrollbare container waren traditioneel complexe scroll-event-listeners, handmatige berekeningen van scroll-offsets en intersection observers vereist. De gebeurtenis scrollsnapchange biedt een native, efficiënte manier om te detecteren wanneer een scroller op een nieuw snapdoel is terechtgekomen. Dit is handig om zijbalken te synchroniseren of het actieve gedeelte in een inhoudsopgave te markeren.

Implementatie

1. Scrolluitlijning instellen in CSS

De container moet scroll-snap-type gedefinieerd hebben en onderliggende elementen met scroll-snap-align hebben zodat de browser snapdoelen kan bijhouden. In een lang artikel met een inhoudsopgave kun je dit gebruiken om sectiekoppen bovenaan de viewport vast te zetten.

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. Luisteren naar Snap-wijzigingen

Gebruik de gebeurtenis scrollsnapchange in de scrollcontainer om te reageren wanneer de gebruiker klaar is met scrollen en de browser naar een nieuw element springt. In onze TOC-demo gebruiken we dit om de actieve link in de zijbalk te markeren.

<!-- 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);
  }
});

Toegankelijkheid

Zorg dat de links in de zijbalk aria-current="true" of aria-current="location" gebruiken als ze actief zijn voor een inhoudsopgave.

Wees ook voorzichtig als je de waarde mandatory gebruikt voor scroll-snap-type, omdat content tussen snap-points ontoegankelijk kan worden als deze langer is dan het scherm.

Reservestrategieën

Als scrollsnapchange niet wordt ondersteund, gebruik je IntersectionObserver om te detecteren welk element op dit moment bovenaan de scroller staat. Dit is ander gedrag dan scrollsnapchange, omdat dit wordt geactiveerd terwijl er wordt gescrold, in plaats van alleen wanneer het scrollen is gestopt.

// 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));
}