PerformanceEventTiming

Limited availability

This feature is not Baseline because it does not work in some of the most widely-used browsers.

PerformanceEventTiming はイベントタイミング API のインターフェイスで、ユーザー操作によって発生させる特定のイベント型の待ち時間に関する分析結果を提供します。

解説

この API により、特定のイベント型(下記参照)のイベントのタイムスタンプと処理時間が提供されるので、遅いイベントを可視化することができるようになります。例えば、ユーザー操作からそのイベントハンドラーが始まるまでの時間や、イベントハンドラーが実行するまでにかかる時間を監視することができます。

この API は、first input delay (FID) (ユーザーがこのアプリを最初に操作した時点から、ブラウザーが実際にその操作に応答することができるまでの時間)を測定するのに特に有益です。

通常、 PerformanceEventTiming オブジェクトを扱うには、 PerformanceObserver インスタンスを作成し、 observe() メソッドを呼び出して、 type オプションに "event" または "first-input" を渡します。 PerformanceObserver オブジェクトのコールバックは、 PerformanceEventTiming オブジェクトのリストとともに呼び出されます。例えば下記の例をご覧ください。

既定では、PerformanceEventTiming 項目は duration が 104ms 以上の場合に公開されます。調査によると、 100ms 以内に処理されないユーザー入力は遅いと考えられており、 104ms は 100ms より大きい最初の 8 の倍数です(セキュリティ上の理由から、この API は最も近い 8 の倍数に丸められます)。 しかし、observe() メソッドの durationThreshold オプションを使用して、PerformanceObserver を別の閾値に設定することができます。

このインターフェイスは、親である PerformanceEntry のメソッドとプロパティを継承しています。

PerformanceEntry PerformanceEventTiming

公開されるイベント

イベントタイミング API で公開されるイベント型は以下の通りです。

クリックイベント auxclick, click, contextmenu, dblclick
変換イベント compositionend, compositionstart, compositionupdate
ドラッグ & ドロップイベント dragend, dragenter, dragleave, dragover, dragstart, drop
入力イベント beforeinput, input
キーボードイベント keydown, keypress, keyup
マウスイベント mousedown, mouseenter, mouseleave, mouseout, mouseover, mouseup
ポインターイベント pointerover, pointerenter, pointerdown, pointerup, pointercancel, pointerout, pointerleave, gotpointercapture, lostpointercapture
タッチイベント touchstart, touchend, touchcancel

なお、連続イベントであり、この時点では意味のあるイベントカウントやパフォーマンス指標を得ることができないため、リストに含まれていないイベントがあります。 mousemove, pointermovepointerrawupdatetouchmovewheeldrag です。

公開されるすべてのイベントのリストを取得するには、 performance.eventCounts マップのキーを見ていくこともできます。

js
const exposedEventsList = [...performance.eventCounts.keys()];

コンストラクター

このインターフェイスは自分自身でコンストラクター持っていません。 PerformanceEventTiming インターフェイスが保持している情報を取得する一般的な方法については、下記の例を参照してください。

インスタンスプロパティ

このインターフェイスは、イベントタイミングパフォーマンス項目型の以下の PerformanceEntry プロパティを、次のように修飾子して拡張します。

PerformanceEntry.duration 読取専用

DOMHighResTimeStamp で、 startTime から次の描画までの時間(8ms に丸めたもの)を返します。

PerformanceEntry.entryType 読取専用

"event" (長いイベント)または "first-input" (最初のユーザー操作)を返します。

PerformanceEntry.name 読取専用

関連するイベントの型を返します。

PerformanceEntry.startTime 読取専用

DOMHighResTimeStamp で、関連付けられたイベントの timestamp プロパティを表す値を返します。これはイベントが作成された時刻であり、ユーザーの操作が発生した時刻のプロキシーと考えることができます。

このインターフェイスは以下のプロパティにも対応しています。

PerformanceEventTiming.cancelable 読取専用

関連するイベントの cancelable プロパティを返します。

PerformanceEventTiming.interactionId 読取専用 Experimental

関連するイベントを発生させたユーザー操作を一意に識別する ID を返します。

PerformanceEventTiming.processingStart 読取専用

イベント配信が開始された時刻を表す DOMHighResTimeStamp を返します。ユーザー操作からイベントハンドラーが実行し始めるまでの時間を計測するには、 processingStart-startTime を計算します。

PerformanceEventTiming.processingEnd 読取専用

イベント配信が終わった時刻を表す DOMHighResTimeStamp を返します。イベントハンドラーが実行するのにかかった時間を計測するには、 processingEnd-processingStart を計算します。

PerformanceEventTiming.target 読取専用

関連付けられたイベントの最後の対象が除去されていない場合、それを返します。

インスタンスメソッド

PerformanceEventTiming.toJSON()

この PerformanceEventTiming オブジェクトの JSON 表現を返します。

イベントタイミング情報の取得

イベントのタイミング情報を取得するには、 PerformanceObserver のインスタンスを作成し、 observe() メソッドを呼び出して、 type オプションの値として "event" または "first-input" を渡してください。また、ユーザーエージェントが文書の構築中にバッファリングしたイベントにアクセスするには、 bufferedtrue に設定する必要があります。 PerformanceObserver オブジェクトのコールバックは、 PerformanceEventTiming オブジェクトのリストとともに呼び出されます。

js
const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    // 時間全体
    const duration = entry.duration;

    // 入力遅延(イベント処理前)
    const delay = entry.processingStart - entry.startTime;

    // 同期イベント処理時間
    // (配信の開始と終了)
    const eventHandlerTime = entry.processingEnd - entry.processingStart;
    console.log(`Total duration: ${duration}`);
    console.log(`Event delay: ${delay}`);
    console.log(`Event handler duration: ${eventHandlerTime}`);
  });
});

// イベントのオブザーバーを登録
observer.observe({ type: "event", buffered: true });

異なる durationThreshold を設定することもできます。既定では 104ms で、最小可能な閾値は 16ms です。

js
observer.observe({ type: "event", durationThreshold: 16, buffered: true });

First Input Delay (FID) の報告

first input delay または FID は、ユーザーが最初にページを操作した時(つまり、リンクをクリックしたりボタンをタップしたりした時)から、その操作に応答してブラウザーが実際にイベントハンドラーの処理を始めることができるまでの時刻を測定します。

js
// Keep track of whether (and when) the page was first hidden, see:
// https://github.com/w3c/page-visibility/issues/29
// NOTE: ideally this check would be performed in the document <head>
// to avoid cases where the visibility state changes before this code runs.
let firstHiddenTime = document.visibilityState === "hidden" ? 0 : Infinity;
document.addEventListener(
  "visibilitychange",
  (event) => {
    firstHiddenTime = Math.min(firstHiddenTime, event.timeStamp);
  },
  { once: true },
);

// Sends the passed data to an analytics endpoint. This code
// uses `/analytics`; you can replace it with your own URL.
function sendToAnalytics(data) {
  const body = JSON.stringify(data);
  // Use `navigator.sendBeacon()` if available,
  // falling back to `fetch()`.
  (navigator.sendBeacon && navigator.sendBeacon("/analytics", body)) ||
    fetch("/analytics", { body, method: "POST", keepalive: true });
}

// Use a try/catch instead of feature detecting `first-input`
// support, since some browsers throw when using the new `type` option.
// https://webkit.org/b/209216
try {
  function onFirstInputEntry(entry) {
    // Only report FID if the page wasn't hidden prior to
    // the entry being dispatched. This typically happens when a
    // page is loaded in a background tab.
    if (entry.startTime < firstHiddenTime) {
      const fid = entry.processingStart - entry.startTime;

      // Report the FID value to an analytics endpoint.
      sendToAnalytics({ fid });
    }
  }

  // Create a PerformanceObserver that calls
  // `onFirstInputEntry` for each entry.
  const po = new PerformanceObserver((entryList) => {
    entryList.getEntries().forEach(onFirstInputEntry);
  });

  // Observe entries of type `first-input`, including buffered entries,
  // i.e. entries that occurred before calling `observe()` below.
  po.observe({
    type: "first-input",
    buffered: true,
  });
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

仕様書

Specification
Event Timing API
# sec-performance-event-timing

ブラウザーの互換性

BCD tables only load in the browser