Détecter l'état de visibilité initial

Il est essentiel de déterminer si une page a été initialement chargée en arrière-plan (par exemple, ouverte dans un nouvel onglet en arrière-plan) pour surveiller précisément les performances. Les chargements de page en arrière-plan ont souvent un rendu retardé et des temps de métriques plus longs (comme First Contentful Paint). En identifiant ces pages, vous pouvez les exclure des analyses de performances pour éviter de fausser les données.

La méthode la plus précise pour mesurer cela consiste à utiliser l'API VisibilityStateEntry, qui enregistre de manière fiable les changements de visibilité dans la chronologie des performances du navigateur, quel que soit le moment où votre script s'exécute.

Détecter la visibilité initiale et le temps en arrière-plan

OBLIGATOIRE : Utilisez performance.getEntriesByType('visibility-state') pour accéder à l'historique exact de la visibilité. Ne vous fiez pas uniquement à la vérification de document.visibilityState au moment de l'exécution, car elle est susceptible de provoquer des conditions de concurrence.

/**
 * Accurately determines visibility state history using the Performance API.
 */
function getVisibilityInfo() {
  // Retrieve VisibilityStateEntry members:
  const entries = performance.getEntriesByType('visibility-state');
  
  if (entries.length > 0) {
    const firstEntry = entries[0];
    
    // If the first performance entry for visibility is 'hidden',
    // the page was loaded in the background.
    const initiallyBackgrounded = firstEntry.name === 'hidden';
    
    // Find the precise, high-resolution timestamp of when the page 
    // was first backgrounded.
    let timeBackgrounded = null;
    for (const entry of entries) {
      if (entry.name === 'hidden') {
        // entry.startTime is used because it provides the exact browser 
        // timestamp of the visibility change, which is required for precision
        timeBackgrounded = entry.startTime;
        break;
      }
    }

    return {
      initiallyBackgrounded,
      timeBackgrounded
    };
  }
}

Stratégies de remplacement

Pour les environnements non compatibles, vous pouvez vérifier la propriété document.visibilityState ou écouter l'événement visibilitychange.

OBLIGATOIRE : Vous devez comprendre que cette approche de remplacement est souvent très imprécise pour déterminer l'état initial en arrière-plan. Étant donné que les scripts peuvent se charger et s'exécuter de manière asynchrone, une page peut être ouverte dans un onglet en arrière-plan, puis mise au premier plan par l'utilisateur avant que le téléchargement et l'exécution de votre script ne soient terminés. Lorsque votre script s'exécute enfin, document.visibilityState est lu de manière synchrone en tant que 'visible', et vous supposez à tort que la page a été chargée au premier plan, en ignorant complètement son état masqué initial. De plus, le code temporel de remplacement ne dispose pas de la précision interne de l'API Performance. Si la précision est une priorité, n'utilisez pas l'option de remplacement.

/**
 * Fallback implementation using document.visibilityState.
 * This approach is prone to race conditions if the script loads asynchronously.
 */
function getFallbackVisibilityInfo() {
  // Check the state exactly when this script executes.
  // This will fail to detect an initial background state if the user 
  // foregrounded the page before this script executed.
  let initiallyBackgrounded = document.visibilityState === 'hidden';
  
  // If it's hidden now, we approximate that it was hidden from load (time 0).
  let timeBackgrounded = initiallyBackgrounded ? 0 : null;

  // Listen for future visibility changes to capture if it is backgrounded later.
  document.addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'hidden' && timeBackgrounded === null) {
      // performance.now() is used here as a fallback, but it only gives 
      // us the time the event listener fired, not the precise internal 
      // browser time the visibility actually changed.
      timeBackgrounded = performance.now();
    }
  });

  return {
    get initiallyBackgrounded() { return initiallyBackgrounded; },
    get timeBackgrounded() { return timeBackgrounded; }
  };
}

// Modern implementation using VisibilityStateEntry API.
function getVisibilityInfo() {
  // Code omitted here would be the same modern
  // implementation shown earlier in this guide
  // ...
}

// DO: Detect if the VisibilityStateEntry API is available
if ('VisibilityStateEntry' in window) {
  // DO: If VisibilityStateEntry is available, use it first:
  getVisibilityInfo();
} else {
  // DO: If VisibilityStateEntry is unavailable, fall back to `document.visibilityState`:
  getFallbackVisibilityInfo();
}