Efficiënte achtergrondverwerking

Webfuncties: content-visibility

Zware achtergrondtaken onderbreken als een component niet wordt gerenderd door de browser om systeembronnen en batterijduur te sparen.

Overzicht

Met de eigenschap content-visibility: auto kan de browser weergaveberekeningen overslaan voor elementen die zich ver buiten de viewport bevinden. Als de browser besluit de weergave van een element over te slaan of te hervatten, wordt de gebeurtenis contentvisibilityautostatechange geactiveerd voor dat element.

Door naar deze gebeurtenis te luisteren, kun je dure bewerkingen zoals <canvas>-animaties, WebGL-rendering of hoogfrequente WebSocket-gegevenspolling onderbreken wanneer ze niet nodig zijn en ze net op tijd hervatten wanneer de browser zich voorbereidt om de content weer te geven.

contentvisibilityautostatechange versus IntersectionObserver

Het is belangrijk om te weten wanneer je welke API moet gebruiken:

  • Gebruik IntersectionObserver voor app-logica die is gekoppeld aan de exacte visuele zichtbaarheid van een element in het kijkvenster (bijv. gegevens voor lazy loading, triggers voor oneindig scrollen).
  • Gebruik contentvisibilityautostatechange voor werk dat veel rendering vereist (zoals complexe canvasupdates of zware DOM-mutaties). Deze gebeurtenis is rechtstreeks gekoppeld aan de interne weergavelevenscyclus van de browser. De browser begint vaak met de weergave van een element voordat het daadwerkelijk op het scherm verschijnt (de pre-render-marge). Deze gebeurtenis geeft aan wanneer dat gebeurt, zodat je content klaar is om te worden bekeken.

Implementatie

1. CSS-contentzichtbaarheid toepassen

Stel content-visibility: auto in voor de zware container en voer een tijdelijke aanduiding in om te voorkomen dat de scrollbalk verspringt.

.heavy-component {
  /* Defer rendering work when off-screen */
  content-visibility: auto;
  
  /* Mandatory: Provide a placeholder size to prevent layouts shifts.
    - 'auto' is optional and enables the browser to remember the actual size
      once rendered. It must be paired with a <length> value to be used for
      the first render.
    - 'none' tells the browser not to apply any intrinsic width to this element.
      It can be used for either the height or the width value.
    - '500px' is the estimated height of this element. This can be any valid
      CSS <length> value. Replace it with the expected height of your
      component.
   */
  contain-intrinsic-size: auto none auto 500px;
}

2. Luisteren naar statuswijzigingen

Voeg een event-listener toe voor contentvisibilityautostatechange om achtergrondtaken te pauzeren of te hervatten.

const component = document.querySelector('.heavy-component');

// Option 1: Direct listener (recommended)
component.addEventListener('contentvisibilityautostatechange', (event) => {
  if (event.skipped) {
    // The browser skipped rendering this content.
    // DO NOT perform heavy mutations or animation loops here.
    stopSimulation();
    pauseWebSocketPolling();
  } else {
    // The browser is about to render the content.
    // Resume your work so it is ready when visible.
    startSimulation();
    resumeWebSocketPolling();
  }
});

// Option 2: Capturing listener for event delegation
document.addEventListener('contentvisibilityautostatechange', (event) => {
  if (event.target.matches('.heavy-component')) {
    if (event.skipped) {
      stopSimulation();
    } else {
      startSimulation();
    }
  }
}, { capture: true });

Reservestrategieën

De property content-visibility en de bijbehorende gebeurtenis contentvisibilityautostatechange zijn progressive enhancements. In browsers die deze niet ondersteunen:

  • De CSS-eigenschap wordt genegeerd en de content wordt normaal weergegeven.
  • De gebeurtenis wordt nooit geactiveerd, dus achtergrondtaken blijven gewoon lopen zonder optimalisatie.

Als je het onderbreken van taken in oudere browsers moet ondersteunen, kun je terugvallen op IntersectionObserver als ruwe benadering. Zo bespaar je ook batterij en CPU op oudere apparaten.

// Fallback using IntersectionObserver for older browsers
const target = document.getElementById('target-container');

// Check if content-visibility is supported
const isSupported = 'contentVisibility' in document.documentElement.style;

if (!isSupported) {
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        // The element is close to the screen. Start work!
        startSimulation();
      } else {
        // The element is far away. Pause work!
        stopSimulation();
      }
    });
  }, {
    // Use rootMargin to start rendering before it hits the screen
    rootMargin: '200px'
  });

  observer.observe(target);
}