レイアウトのずれを特定して修正する方法について学びます。
公開日: 2021 年 3 月 11 日、最終更新日: 2025 年 2 月 7 日
この記事の前半では、レイアウト シフトのデバッグ用ツールについて説明します。後半では、レイアウト シフトの原因を特定する際の考え方について説明します。
ツール
レイアウト シフトは、Layout Instability API を使用してデバッグできます。また、この API のデータをよりわかりやすい形式で要約する DevTools などのツールを使用してデバッグすることもできます。
Layout Instability API
Layout Instability API は、レイアウトのずれを測定して報告するためのブラウザ メカニズムです。DevTools を含む、レイアウト シフトのデバッグ用ツールはすべて、最終的には Layout Instability API 上に構築されています。ただし、Layout Instability API を直接使用すると、柔軟性が高いため強力なデバッグ ツールになります。
用途
Cumulative Layout Shift(CLS) を測定するコード スニペットは、レイアウト シフトのデバッグにも使用できます。次のスニペットは、レイアウトのずれに関する情報をコンソールに記録します。このログを調べると、レイアウト シフトが発生した日時、場所、方法に関する情報を確認できます。
let cls = 0;
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
if (!entry.hadRecentInput) {
cls += entry.value;
console.log('Current CLS value:', cls, entry);
}
}
}).observe({type: 'layout-shift', buffered: true});
このスクリプトを実行する際は、次の点に注意してください。
buffered: true
オプションは、PerformanceObserver
がオブザーバーの初期化前に作成されたパフォーマンス エントリについて、ブラウザのパフォーマンス エントリ バッファをチェックする必要があることを示します。その結果、PerformanceObserver
は、初期化の前後に発生したレイアウト シフトを報告します。コンソール ログを調べる際は、この点に注意してください。レイアウト シフトが最初に急増した場合、多数のレイアウト シフトが突然発生したのではなく、レポートのバックログが反映されている可能性があります。- パフォーマンスに影響しないように、
PerformanceObserver
はメインスレッドがアイドル状態になるまで待機してから、レイアウトのずれを報告します。そのため、メインスレッドの負荷によっては、レイアウト シフトが発生してからコンソールにログに記録されるまでに若干の遅延が生じることがあります。 - このスクリプトは、ユーザー入力から 500 ミリ秒以内に発生したレイアウト シフトを無視するため、CLS にはカウントされません。
レイアウト シフトに関する情報は、LayoutShift
インターフェースと LayoutShiftAttribution
インターフェースの 2 つの API を組み合わせて報告されます。これらのインターフェースについては、以降のセクションで詳しく説明します。
LayoutShift
各レイアウト シフトは LayoutShift
インターフェースを使用して報告されます。エントリの内容は次のようになります。
duration: 0
entryType: "layout-shift"
hadRecentInput: false
lastInputTime: 0
name: ""
sources: (3) [LayoutShiftAttribution, LayoutShiftAttribution, LayoutShiftAttribution]
startTime: 11317.934999999125
value: 0.17508567530168798
前のエントリは、3 つの DOM 要素の位置が変更されたレイアウト シフトを示しています。このレイアウト シフトのレイアウト シフト スコアは 0.175
でした。
レイアウト シフトのデバッグに最も関連する LayoutShift
インスタンスのプロパティは次のとおりです。
プロパティ | 説明 |
---|---|
sources |
sources プロパティには、レイアウト シフト中に移動した DOM 要素のリストが表示されます。この配列には最大 5 つのソースを含めることができます。レイアウト シフトの影響を受ける要素が 5 つを超える場合は、レイアウトの安定性への影響に基づいて、レイアウト シフトの原因として 5 つが報告されます。この情報は、LayoutShiftAttribution インターフェースを使用して報告されます(後で詳しく説明します)。 |
value |
value プロパティは、特定のレイアウト シフトのレイアウト シフト スコアを報告します。 |
hadRecentInput |
hadRecentInput プロパティは、ユーザー入力から 500 ミリ秒以内にレイアウト シフトが発生したかどうかを示します。 |
startTime |
startTime プロパティは、レイアウト シフトが発生したタイミングを示します。startTime はミリ秒単位で、ページの読み込みが開始された時刻を基準に測定されます。 |
duration |
duration プロパティは常に 0 に設定されます。このプロパティは PerformanceEntry インターフェースから継承されます(LayoutShift インターフェースは PerformanceEntry インターフェースを拡張します)。ただし、レイアウト シフト イベントには継続時間の概念が適用されないため、0 に設定されます。PerformanceEntry インターフェースの詳細については、仕様をご覧ください。 |
LayoutShiftAttribution
LayoutShiftAttribution
インターフェースは、単一の DOM 要素の単一シフトを記述します。レイアウト シフト中に複数の要素がシフトする場合、sources
プロパティには複数のエントリが含まれます。
たとえば、次の JSON は、1 つのソース(<div id='banner'>
DOM 要素が y: 76
から y:246
に下方にシフト)によるレイアウト シフトに対応しています。
// ...
"sources": [
{
"node": "div#banner",
"previousRect": {
"x": 311,
"y": 76,
"width": 4,
"height": 18,
"top": 76,
"right": 315,
"bottom": 94,
"left": 311
},
"currentRect": {
"x": 311,
"y": 246,
"width": 4,
"height": 18,
"top": 246,
"right": 315,
"bottom": 264,
"left": 311
}
}
]
node
プロパティは、シフトされた HTML 要素を識別します。DevTools でこのプロパティにカーソルを合わせると、対応するページ要素がハイライト表示されます。
previousRect
プロパティと currentRect
プロパティは、ノードのサイズと位置を報告します。
x
座標とy
座標は、要素の左上隅の x 座標と y 座標をそれぞれ報告します。width
プロパティとheight
プロパティは、要素の幅と高さをそれぞれ報告します。top
、right
、bottom
、left
プロパティは、要素の特定のエッジに対応する x 座標値または y 座標値を報告します。つまり、top
の値はy
と等しく、bottom
の値はy+height
と等しい。
previousRect
のすべてのプロパティが 0 に設定されている場合、要素がビュー内に移動したことを意味します。currentRect
のすべてのプロパティが 0 に設定されている場合、要素がビューの外側にシフトされていることを意味します。
これらの出力を解釈する際に最も重要なことの 1 つは、[ソース] としてリストされている要素は、レイアウト シフト中にシフトされた要素であるということです。ただし、これらの要素は、レイアウトの不安定性の「根本原因」に間接的にしか関連していない可能性があります。次に例を示します。
例 1
このレイアウト シフトは、要素 B という 1 つのソースで報告されます。ただし、このレイアウトのずれの根本原因は、要素 A のサイズの変更です。
例 2
この例のレイアウト シフトは、要素 A と要素 B の 2 つのソースで報告されます。このレイアウト シフトの根本原因は、要素 A の位置の変更です。
例 3
この例のレイアウト シフトは、1 つのソース(要素 B)で報告されます。要素 B の位置を変更した結果、このレイアウト シフトが発生しました。
例 4
この例では、要素 B のサイズは変更されますが、レイアウト シフトは発生しません。
Layout Instability API によって DOM の変更が報告される方法のデモをご覧ください。
DevTools
DevTools には、レイアウトのずれをデバッグするためのツールがいくつか用意されています。
[パフォーマンス] パネル
[パフォーマンス] パネルのライブ指標ビューでは、ページを操作しながら CLS スコアをモニタリングし、レイアウト シフトの原因となる操作を特定できます。
![Chrome DevTools のパフォーマンス パネルのライブ指標画面に表示されているレイアウト シフト レコード。](https://web.developers.google.cn/static/articles/debug-layout-shifts/image/live-metrics-cls.png?hl=ja)
レイアウト シフトを信頼性を持って再現できたら、トレースを行って詳細情報を取得できます。
![Chrome DevTools のパフォーマンス パネルに表示されるレイアウト シフト レコード。](https://web.developers.google.cn/static/articles/debug-layout-shifts/image/devtools-cls-debugging.png?hl=ja)
Layout Shift
クラスタが表示された紫色のバーが追加されます。ダイヤモンドをクリックすると、シフトのアニメーションと [概要] パネルに詳細が表示されます。レイアウト シフトは [レイアウト シフト] トラックでハイライト表示されます。紫色の線はシフトをシフトクラスタにグループ化し、そのクラスタ内の個々のシフトをダイヤモンドで示します。ダイヤモンドのサイズはシフトのサイズに比例するため、最も大きなシフトに注目できます。
シフトをクリックすると、シフトのアニメーションが表示されたポップアップが表示され、シフトする要素が紫色でハイライト表示されます。
また、Layout Shift
レコードの [概要] ビューには、開始時間、シフトスコア、シフトされた要素が表示されます。これは、読み込み CLS の問題を詳しく把握するのに特に役立ちます。この問題は、再読み込みのパフォーマンス プロファイルで簡単に再現できます。
また、左側の [分析情報] パネルに表示される [レイアウト移動の原因] インサイトにもリンクします。このインサイトの上部には合計 CLS と、レイアウト移動の考えられる原因が表示されます。
[パフォーマンス] パネルの使用方法については、パフォーマンス分析リファレンスをご覧ください。
レイアウト シフト領域をハイライト表示
レイアウト シフト領域をハイライト表示すると、ページで発生するレイアウト シフトの場所とタイミングをすばやく把握できます。
DevTools でレイアウト シフト領域を有効にするには、[設定] > [その他のツール] > [レンダリング] > [レイアウト シフト領域] に移動し、デバッグするページを更新します。レイアウト シフトが発生した領域は、紫色でハイライト表示されます。
レイアウトのずれの原因を特定するための考え方
レイアウト シフトが発生するタイミングや方法に関係なく、レイアウト シフトの原因を特定するには、次の手順を行います。これらの手順は Lighthouse の実行で補完できますが、Lighthouse で特定できるのは、最初のページ読み込み中に発生したレイアウト シフトのみです。また、Lighthouse は、レイアウトのずれの原因の一部(明示的な幅と高さがない画像要素など)についてのみ、推奨事項を提供できます。
レイアウト シフトの原因を特定する
レイアウトのずれは、次のイベントによって発生する可能性があります。
- DOM 要素の位置の変更
- DOM 要素のサイズの変更
- DOM 要素の挿入または削除
- レイアウトをトリガーするアニメーション
特に、シフトされた要素の直前の DOM 要素は、レイアウト シフトの「原因」となる可能性が高い要素です。したがって、レイアウト シフトが発生した原因を調査する際は、次の点を考慮してください。
- 前の要素の位置やサイズに変更はありましたか?
- シフトされた要素の前に DOM 要素が挿入または削除されたか?
- 移動された要素の位置が明示的に変更されましたか?
前の要素がレイアウト シフトの原因ではなかった場合は、他の前の要素と近くの要素を検討して検索を続けます。
また、レイアウトのずれの方向と距離から、根本原因に関するヒントを得ることができます。たとえば、下方向への大きなシフトは DOM 要素の挿入を示すことが多く、1 ピクセルまたは 2 ピクセルのレイアウト シフトは、競合する CSS スタイルの適用や、ウェブフォントの読み込みと適用を示すことが多くあります。
![フォント交換によって発生したレイアウト シフトを示す図](https://web.developers.google.cn/static/articles/debug-layout-shifts/image/diagram-showing-layout-s-60ec3d8c95616.png?hl=ja)
レイアウト移動イベントを引き起こす最も一般的な動作は次のとおりです。
要素の位置の変更(他の要素の移動によるものではない)
このような変更は、次のようなことが原因で発生することがよくあります。
- 遅れて読み込まれるスタイルシートや、以前に宣言されたスタイルを上書きするスタイルシート。
- アニメーションと遷移の効果。
要素のサイズの変更
このような変更は、次のようなことが原因で発生することがよくあります。
- 遅れて読み込まれるスタイルシートや、以前に宣言されたスタイルを上書きするスタイルシート。
width
属性とheight
属性のない画像と iframe で、「スロット」のレンダリング後に読み込まれる。- テキストのレンダリング後にフォントを切り替える
width
属性またはheight
属性のないテキスト ブロック。
DOM 要素の挿入または削除
多くの場合、これは次のような原因で発生します。
- 広告やその他のサードパーティの埋め込みの挿入。
- バナー、アラート、モーダルの挿入。
- 既存のコンテンツの上に追加のコンテンツを読み込む無限スクロールなどの UX パターン。
レイアウトをトリガーするアニメーション
一部のアニメーション効果はレイアウトをトリガーすることがあります。一般的な例としては、CSS の transform
プロパティを使用するのではなく、top
や left
などのプロパティをインクリメントして DOM 要素を「アニメーション化」する場合などがあります。詳細については、高パフォーマンスの CSS アニメーションを作成する方法をご覧ください。
レイアウト シフトを再現する
再現できないレイアウトのずれは修正できません。サイトのレイアウトの安定性を把握するための最も簡単で効果的な方法の一つは、レイアウトのずれをトリガーすることを目的として、5 ~ 10 分間サイトを操作することです。コンソールを開いたまま、Layout Instability API を使用してレイアウトのずれを報告します。
レイアウトのずれが見つけにくい場合は、別のデバイスと接続速度でこの演習を繰り返すことを検討してください。特に、接続速度が遅い場合は、レイアウトのずれを簡単に特定できます。また、debugger
ステートメントを使用すると、レイアウト シフトを簡単にステップスルーできます。
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
if (!entry.hadRecentInput) {
cls += entry.value;
debugger;
console.log('Current CLS value:', cls, entry);
}
}
}).observe({type: 'layout-shift', buffered: true});
最後に、開発環境で再現できないレイアウトの問題については、Layout Instability API と任意のフロントエンド ロギング ツールを併用して、これらの問題に関する詳細情報を収集することを検討してください。ページ上で最も大きく移動した要素を追跡する方法のコード例をご覧ください。