Largest Contentful Paint APIで最大コンテンツの描画時間を計測する
Largest Contentful Paint APIはビューポート内で最も大きなコンテンツ要素が描画されるまでの時間を計測するためのAPIです。Web VitalsのLCP指標の計測に使用され、ページの読み込み体験を評価する重要な機能です。
はじめに
Webページの体験において、ユーザーが「コンテンツの表示が完了した」と感じるタイミングは重要です。ファーストビューに大きな画像やテキストブロックが表示されるまでの時間が長いと、ユーザーはページが遅いと感じてしまいます。
Web Vitalsでは、その体験を評価するための指標としてLCP(Largest Contentful Paint)を定義しています。LCPはビューポート内に表示される最大の画像、テキストブロック、動画のレンダリング時間を測定します。
Largest Contentful Paint APIは、そんなLCPの計測に使用されるAPIです。このAPIを利用することで、開発者はページの読み込み体験を詳細に分析し、改善点を特定することができます。
Largest Contentful Paint API
Largest Contentful Paint APIは、ビューポート内で最も大きなコンテンツ要素が描画されるまでの時間の分析結果をLargestContentfulPaintインターフェースで提供します。
LCPの計測のためのAPIなので、img要素、svg内のimage要素、video要素、background-imageでスタイリングされた要素、p要素などのテキスト要素が対象となります。
PerformanceEntryから継承したプロパティ
LargestContentfulPaintはPerformanceEntryを継承しており、startTime、entryTypeなどのプロパティを持ちます。
startTime
後に紹介するLargestContentfulPaint独自のプロパティであるrenderTimeまたはloadTimeのいずれかの時刻を返します。
具体的には基本的にrenderTimeを返すが、renderTimeが0の場合はloadTimeを返します。
entryType
LargestContentfulPaintの場合は"largest-contentful-paint"を返します。
LargestContentfulPaint独自のプロパティ
また、独自のプロパティとして、renderTime、loadTime、size、element、url、idを持ちます。
renderTime
要素が実際にレンダリングされた時間です。
クロスオリジンのリソースでTiming-Allow-Originヘッダーが設定されていない場合は0になります。
loadTime
リソースの読み込みが完了した時間です。
テキスト要素の場合は0になります。
size
要素のサイズをピクセル単位の面積で返します。
この値は要素の可視領域のサイズであり、クリップされた部分やビューポート外の部分は含まれません。
element
イベントのターゲット要素を返します。
要素がDOMから削除された場合や、Shadow DOM内にある場合はnullを返します。
url
リソースを持つ場合、そのリソースのURLを返します。
id
要素のid属性の値を返します。id属性がない場合は空文字列を返します。
基本的な使い方
PerformanceObserverを使用してLCPのタイミング情報を取得します。
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
// 最後のエントリが最終的なLCP
const lastEntry = entries[entries.length - 1];
console.log(`LCP: ${lastEntry.startTime}ms`);
console.log(`要素: ${lastEntry.element?.tagName}`);
console.log(`サイズ: ${lastEntry.size}px²`);
if (lastEntry.url) {
console.log(`リソースURL: ${lastEntry.url}`);
}
});
observer.observe({ type: 'largest-contentful-paint', buffered: true });
Playground
LCPエントリを待機中...ページを再読み込みすると計測されます。
ページ読み込み時のLCP(Largest Contentful Paint)を計測しています。ページを再読み込みすると新しい計測結果が表示されます。
複数のEntry
PerformanceObserverでは複数のentryが報告されます。Largestなコンテンツ要素はどのように取得すれば良いのでしょうか。そもそも、なぜ複数のentryが報告されるのでしょうか。
複数読み込まれるのは、ページの読み込みが進む中で、最大のコンテンツ要素は変化する可能性があるためです。最初は小さなテキストが最大でも、後から大きな画像が読み込まれると、その画像の要素は新たなentryとして報告されます。
そのため、PerformanceObserverは新しいLCP候補が見つかるたびにentryを報告します。つまり、Largestなコンテンツ要素は、PerformanceObserverで受け取ったentryの中で最後のものとなります。
let lcpValue = 0;
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
lcpValue = lastEntry.startTime;
});
observer.observe({ type: 'largest-contentful-paint', buffered: true });
// ユーザーインタラクション時にLCPを確定
['keydown', 'click', 'pointerdown'].forEach((type) => {
window.addEventListener(type, () => {
console.log(`最終LCP: ${lcpValue}ms`);
observer.disconnect();
}, { once: true });
});
最終的な状態を確定することはできないので、ここではユーザーの最初のインタラクション時にLCPを確定しています。
おわりに
Largest Contentful Paint APIを紹介しました。
Web APIを用いて、Web Vitalsの計測ができると実際のユーザーの環境における計測が行いやすくとても便利に感じます。
Baseline 2025に加わっていますので、LCPの改善やパフォーマンス監視が必要な場面で活用してみてください。