Rileva stato di visibilità iniziale

Determinare se una pagina è stata inizialmente caricata in background (ad esempio, aperta in una nuova scheda in background) è fondamentale per un monitoraggio accurato delle prestazioni. Le pagine caricate in background spesso hanno un rendering ritardato e tempi di metrica più lunghi (come First Contentful Paint). L'identificazione di queste pagine ti consente di filtrarle dalle analisi del rendimento per evitare dati distorti.

Il modo più accurato per misurare questo valore è utilizzare l'API VisibilityStateEntry, che registra in modo affidabile le modifiche alla visibilità nella cronologia delle prestazioni del browser, indipendentemente da quando viene effettivamente eseguito lo script.

Rilevamento della visibilità iniziale e del tempo in background

MANDATORY: Use performance.getEntriesByType('visibility-state') to access the exact visibility history. Non fare affidamento esclusivamente sul controllo di document.visibilityState al momento dell'esecuzione, poiché è soggetto a race condition.

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

Strategie di riserva

Per gli ambienti non supportati, puoi tornare a controllare la proprietà document.visibilityState o ad ascoltare l'evento visibilitychange.

OBBLIGATORIO: devi comprendere che questo approccio di riserva è spesso molto impreciso per determinare lo stato iniziale in background. Poiché gli script possono essere caricati ed eseguiti in modo asincrono, una pagina potrebbe essere aperta in una scheda in background e poi portata in primo piano dall'utente prima che lo script abbia terminato il download e l'esecuzione. Quando lo script viene finalmente eseguito, document.visibilityState verrà letto in modo sincrono come 'visible' e presupporrai erroneamente che la pagina sia stata caricata in primo piano, perdendo completamente il suo stato nascosto iniziale. Inoltre, il timestamp di riserva non ha la precisione interna dell'API Performance. Se la precisione è una priorità elevata, non utilizzare il valore di riserva.

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