初期表示状態を検出する

ページが最初にバックグラウンドで読み込まれたかどうか(新しいバックグラウンド タブで開かれたかどうかなど)を判断することは、パフォーマンスを正確にモニタリングするうえで重要です。バックグラウンドで読み込まれたページでは、レンダリングが遅延し、指標時間(First Contentful Paint など)が長くなることがよくあります。これらのページを特定することで、パフォーマンス分析から除外してデータの偏りを回避できます。

これを測定する最も正確な方法は、VisibilityStateEntry API を使用することです。この API は、スクリプトが実際に実行されるタイミングに関係なく、ブラウザのパフォーマンス タイムラインで可視性の変化を確実に記録します。

初期の可視性とバックグラウンド時間の検出

必須: performance.getEntriesByType('visibility-state') を使用して、正確な公開設定履歴にアクセスします。実行時に document.visibilityState を確認することだけに依存しないでください。競合状態の影響を受けやすくなります。

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

代替戦略

サポートされていない環境では、document.visibilityState プロパティを確認するか、visibilitychange イベントをリッスンするようにフォールバックできます。

必須: このフォールバック アプローチは、初期のバックグラウンド状態を判断するうえで精度が非常に低いことが多いことを理解しておく必要があります。スクリプトは非同期で読み込まれて実行されるため、スクリプトのダウンロードと実行が完了する前に、ページがバックグラウンド タブで開かれ、ユーザーによってフォアグラウンドに移動される可能性があります。スクリプトが最終的に実行されると、document.visibilityState は 'visible' として同期的に読み取られ、ページがフォアグラウンドで読み込まれたと誤って想定され、最初の非表示状態が完全に失われます。さらに、フォールバック タイムスタンプには、Performance API の内部精度がありません。精度を重視する場合は、フォールバックを使用しないでください。

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