レイアウト不安定性 API

コミュニティグループ報告書ドラフト,

このバージョン:
https://wicg.github.io/layout-instability
課題追跡:
GitHub
仕様内インライン
編集者:
(Google)
(Google)
(Google)

概要

この文書は、ページ上の要素の移動に基づいて、Web ページの作者に自分のページの安定性に関する洞察を提供する API を定義する。

この文書のステータス

この仕様は Web Platform Incubator Community Group によって公開された。 これは W3C 標準ではなく、W3C 標準化トラック上のものでもない。 W3C Community Contributor License Agreement (CLA) の下では、限定的なオプトアウトがあり、その他の条件が適用されることに注意してほしい。 詳細は W3C Community and Business Groups を参照。

1. はじめに

このセクションは非規範的です。

ウェブページ上で DOM 要素が移動することは、ユーザー体験を損なう原因となり、 現在のウェブで頻繁に発生しています。この移動は、コンテンツが非同期に 読み込まれてページ上の他の要素を押しのけてしまうことが主な要因です。

Layout Instability API は、ユーザーのセッション中の各アニメーションフレーム ごとに値(「レイアウトシフト」)を報告することで、こうした不安定なページを 特定します。本仕様では、ユーザーエージェントがレイアウトシフト値を 計算する方法を提示します。

レイアウトシフト値は、ある時点でのレイアウト不安定性の深刻度に大まかに 対応することが期待されています。その計算方法は、不安定さの影響を受けた 領域の面積と、ページ上の要素が移動した距離の両方を考慮します。

本仕様で公開される値は、「レイアウト変更オブザーバー」として 用いることを意図していません。その理由は二つあります。 一つ目は、これらの値は PerformanceObserver に結び付けられており、 サイトのパフォーマンスへの影響を避けるため、必要と判断すれば ユーザーエージェントによってコールバックの実行が遅延される場合があることです。 二つ目は、非常に小さなレイアウトシフトはユーザーエージェントに よって無視される可能性があることです。従って、ウェブサイトの ユーザーに見える挙動へ影響する JavaScript を実行する手段として この API に依存することは推奨されません。

1.1. 累積レイアウトシフト(CLS)

このセクションは非規範的です。

レイアウトシフト値 は単一時点の値を表しますが、ユーザーがページで過ごす期間全体の 不安定性を示す値を持つことも有用です。

そのため、私たちはユーザーエージェントや開発者がそのような表現を得るために計算できる 2 つの値を提案します。(これらの定義は API では公開されないため非規範的です。)

累積レイアウトシフトスコアは、ページのライフタイム全体に対する レイアウト不安定性の深刻度を大まかに示すものとなります。

開発者はこの API を利用し、レポートされた値を合計して DCLS や CLS スコアを計算し、 visibilitychange イベント発火時に 「最終」スコアを取得することができます。

この方法は使用例に示されています。

1.2. 原因帰属

このセクションは非規範的です。

レイアウトシフト値に加えて、この API はアニメーションフレームごとの レイアウトシフト値に最も大きく寄与した最大5つの DOM 要素のサンプリングを 報告します。sources は影響領域が大きい順に並べられており、 最初の要素がレイアウトシフトに最も寄与した要素を表します。

実際の「根本原因」はレイアウトシフトを受けた DOM 要素と 間接的にのみ関係している場合があります。例えば、新しく挿入された 要素によって下のコンテンツがずれた場合、sources 属性には ずれた要素だけが報告され、挿入された要素は含まれません。

ユーザーエージェントが本当の意味での「根本原因」特定に必要な 間接性のレベルまで不安定性の原因を理解することは、現実的でないと考えます。 しかし、この API が提供するシフト要素の単純なレポートでも、 レイアウト不安定発生時の原因究明に取り組む開発者にとって十分価値が あると期待しています。

1.3. 使用例

このセクションは非規範的です。

let perFrameLayoutShiftData = [];
let cumulativeLayoutShiftScore = 0;

function updateCLS(entries) {
  for (const entry of entries) {
    // Only count layout shifts without recent user input.
    if (entry.hadRecentInput)
      return;

    perFrameLayoutShiftData.push({
      score: entry.value,
      timestamp: entry.startTime
    });
    cumulativeLayoutShiftScore += entry.value;

    // Sources are sorted by impact area in descending order.
    // The first element contributed most to the layout shift.
    if (entry.sources && entry.sources.length > 0) {
      console.log('Largest contributing element:', entry.sources[0].node);
    }
  }
}

// Observe all layout shift occurrences.
const observer = new PerformanceObserver((list) => {
  updateCLS(list.getEntries());
});
observer.observe({type: 'layout-shift', buffered: true});

// Send final data to an analytics back end once the page is hidden.
document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') {
    // Force any pending records to be dispatched.
    updateCLS(observer.takeRecords());

    // Send data to your analytics back end (assumes `sendToAnalytics` is
    // defined elsewhere).
    sendToAnalytics({perFrameLayoutShiftData, cumulativeLayoutShiftScore});
  }
});

レイアウトシフトスコアは一つの指標に過ぎず、「ジャンプ感」というユーザー体験と おおよそ相関があります。

開発者はレイアウトシフトスコア間のわずかな違いを気にしすぎないことが推奨されます。 この指標は高精度の値を意図しているわけではなく、計算効率を優先して ユーザーエージェントが精度を犠牲にすることもあります。 さらに、この指標の定義は今後見直される可能性もあります。

2. 用語

2.1. 基本概念

始点(starting point)は、座標空間 C における Node N の始点として次のように定義される:

変形非依存始点(transform-indifferent starting point)は、 Node NC における 始点を、 すべての変形要素変換行列を単位行列とするかのように計算したものとする。

注: ノードがシフトしたかどうかを判断する際には、 トランスフォームを考慮した場合としない場合の始点を比較し、ノードがトランスフォームの変化のみで 不安定になることを防ぐ。ただし、視覚的表現やビューポート外への除外の計算には 常にCSSのトランスフォームが考慮される。

視覚的表現(visual representation)Node N について以下のように定義される:

ある条件が前フレーム時点で(in the previous frame)成立するとき、 それは直近のレイアウトシフト報告アルゴリズムの完了直後の 時点にその条件が真であったことを意味する。

前フレームの始点(previous frame starting point)は、 座標空間CにおけるNode Nについて、 前フレーム時点における 始点を指す。

前フレームの変形非依存始点(previous frame transform-indifferent starting point)は、 座標空間Cにおける Node Nについて、 前フレーム時点 での変形非依存始点を指す。

前フレームの視覚的表現(previous frame visual representation)は、 Node Nについて、 前フレーム時点 での視覚的表現の集合である。

各ユーザーエージェントは、レイアウトシフトと見なすかどうかを判定するのに使われる整数値 意義ありピクセル数(number of pixels to significance)を定義する。 この柔軟性により、ユーザーエージェントはパフォーマンスやユーザー体験を考慮して調整できる。

Aが点B大きく異なる(differs significantly)とは、 AB意義ありピクセル数以上だけ 水平または垂直方向のいずれかで ピクセル単位で異なる場合を指す。

注: Chrome では 意義ありピクセル数 を 3 と定義している。

2.2. 不安定ノード

Node N は座標空間 Cシフトした(has shifted)とみなされるのは、次の場合である:

それ以外の場合、Nシフトしなかった(has not shifted)とみなされる。

Node N不安定候補(unstable-candidate)であるのは次の場合である:

注: スクロール可能オーバーフロー領域に関する条件は、 単なるスクロール操作によってノードが不安定とみなされるのを防ぐためのものです。

Node N は、 不安定候補であって、 インライン・クリップ・クロッサーでない場合、 不安定(unstable)である。

Node Nインライン・クリップ・クロッサー(inline clip crosser)であるのは:

注: インライン・クリップ・クロッサーの例としては、インライン方向に クリップ境界をまたぐようにしてビュー内またはビュー外へ移動する要素が挙げられる。 こうした要素はブロックフロー方向にずれない限り、不安定ノード集合から除外される。 これにより「カルーセル」型UIコントロールのようなものを簡単に構築できる。

不安定ノード集合(unstable node set)は、 Document D において、 D不安定シャドウを含む子孫すべてを含む集合である。

注: 最初のフレームでは、前フレームの始点が存在しないため、どのノードも不安定ノード集合に含まれない。

2.3. レイアウトシフト値

ビューポート基準距離とは、 ビジュアルビューポートの幅ビジュアルビューポートの高さ のうち大きい方である。

移動ベクトルは、Node Nについて、ピクセル単位での2次元オフセットである。

移動距離Node N について、次のうち大きい方である:

最大移動距離Document D不安定ノード集合 内のすべてのNode についての移動距離の最大値、または集合が空の場合は0。

距離率Document Dについて、次の小さい方:

ノード影響領域不安定Node Nに対し、次の点の集合:

影響領域Document Dについて、 不安定ノード集合内の 全Nodeノード影響領域中の すべての点の集合。

影響率Document Dについて、 影響領域の面積を ビューポートの面積で割った値(ビューポートの面積が0なら0)。

注: 影響領域の面積計算は2次元における Klee measure problemにあたる。 掃引線とセグメントツリーを用いて O(n lg n) 時間で (nは不安定ノード数)、このように解ける。

レイアウトシフト値Document Dについて、 影響率距離率を掛けたものとする。

注: レイアウトシフト値は、 レイアウト不安定性がビューポートのどれほどの割合に影響したかと 各要素がどれくらい移動したかの最大値を両方考慮に入れている。 大きな要素が少しだけずれた場合は、ページ全体の不安定感が低い とみなされるケースに対応している。

2.4. 入力除外

除外入力とは、 ドキュメントへのユーザーの積極的な操作を示す入力デバイスからの イベント、またはビューポートのサイズを直接変化させるイベントである。

除外入力には、主に mousedownkeydownpointerdownchangeイベントなどが含まれる。 ただし、フリックやスクロールジェスチャーの開始や更新のみの効果を持つ イベントは除外入力に該当しない。

ユーザーエージェントは、 pointerdownイベント発生後、 そのイベントがフリックもしくはスクロールの開始でないと判明するまで レイアウトシフトの報告を遅延させてもよい。

mousemove および pointermove イベントも除外入力ではない。

3. LayoutShift インターフェイス

[Exposed=Window]
interface LayoutShift : PerformanceEntry {
  readonly attribute double value;
  readonly attribute boolean hadRecentInput;
  readonly attribute DOMHighResTimeStamp lastInputTime;
  readonly attribute FrozenArray<LayoutShiftAttribution> sources;
  [Default] object toJSON();
};

すべての属性値はレイアウトシフトを報告する ステップによって設定される。

sources属性は FrozenArrayLayoutShiftAttribution オブジェクトの配列)を返す。 この配列は影響領域の大きい順にソートされ、先頭要素が最もノード影響領域 の大きな要素、すなわちレイアウトシフトに最も寄与した要素を表す。

Layout Instability APIを実装するユーザーエージェントは、 supportedEntryTypes"layout-shift"Window コンテキストで必ず含めなければならない。 これにより開発者は Layout Instability API のサポートを検知できる。

4. LayoutShiftAttribution インターフェイス

[Exposed=Window]
interface LayoutShiftAttribution {
  readonly attribute Node? node;
  readonly attribute DOMRectReadOnly previousRect;
  readonly attribute DOMRectReadOnly currentRect;
};

注: previousRect および currentRect 属性はCSSピクセル単位で長方形を報告し、 getBoundingClientRect()IntersectionObserverResizeObserver など他のWebプラットフォームAPIと同様である。 これにより、レイアウトシフトと他のDOM測定値の相関が容易になるデバイス非依存の座標系が提供される。

LayoutShiftAttribution は、そのNode対応ノード)と結びついている。

LayoutShiftAttribution インスタンスAnode属性のgetterは、 A対応ノードと そのノードドキュメントを入力に、 要素取得アルゴリズムを呼び出し、その結果を返す。

注: 要素取得アルゴリズムにより、 属性づけられたノードが現在接続されていない、もしくはシャドウルート内である場合は node属性がnullになることが保証される。

要素取得アルゴリズムは Element Timing 仕様から 本仕様で再利用しやすい場所に移すべきである。

要素取得アルゴリズムは Elementではなく Node を受け付けるよう一般化すべきである。

previousRect およびcurrentRect の各属性値は帰属を作成するステップによって設定される。

5. 処理モデル

レンダリングの更新 ステップ内で、Layout Instability API を実装するユーザーエージェントは ペイントタイミングをマーク アルゴリズムの実行後、次の処理を行わなければならない:

  1. すべての fully active なDocument について レイアウトシフトを報告する アルゴリズムを呼ぶ。

5.1. レイアウトシフトを報告する

アクティブなDocument Dに対して レイアウトシフトを報告するとき、以下の手順を実行する:
  1. 現在の Dレイアウトシフト値が0でなければ:

    1. D関連 Realmで 新しい LayoutShift オブジェクトnewEntryを作成する。

    2. newEntryname 属性に "layout-shift" を設定する。

    3. newEntryentryType 属性に "layout-shift" を設定する。

    4. newEntrystartTime 属性に、 現在の高解像度時刻D関連グローバルオブジェクトで取得し設定する。

    5. newEntryduration 属性を0に設定する。

    6. newEntryvalue 属性に 現在のDレイアウトシフト値を設定する。

    7. newEntrylastInputTime 属性に、 もっとも最近の 除外入力の時刻(セッションで発生していなければ0)を設定する。

    8. newEntryhadRecentInput 属性を、lastInputTime の値が直近500ミリ秒未満かどうかで trueまたはfalse で設定する。

    9. newEntrysources 属性を、D に対して レイアウトシフトの要素情報を報告する アルゴリズムの結果で設定する。

    10. PerformanceEntryをキューする 際のオブジェクトとしてnewEntryを用いる。

5.2. レイアウトシフト要素情報の報告

アクティブな Document D に対し レイアウトシフト要素情報を報告する と求められた時、次の手順を実行する:
  1. C を空の リストNode オブジェクト)とする。

  2. D不安定ノード集合の各メンバー N について、次の手順を行う:

    1. もし Nノード影響領域C の任意の要素 existingNodeノード影響領域 の部分集合であるなら、処理を継続する(continue)。

    2. そうでなく、C の任意の要素 existingNode について ノード影響領域Nノード影響領域の部分集合であるものがあれば、 その最初の existingNodeC 内で N置き換える

    3. それ以外で C の要素数が5未満なら、 CにNを追加する。

      注: 「5」という値の選択は任意だが、詳細な要素帰属情報を 提供しつつメモリコストや公開ノードセットの スパム化を抑制するバランスをとっている。

    4. それ以外の場合、次を行う:

      1. smallest を、C 内の他のどの要素よりも ノード影響領域 の面積が大きくない最初の要素とする。

      2. もし Nノード影響領域の面積が smallestノード影響領域 の面積より大きければ、C 内の smallestN置き換える

  3. Cを降順ソートする。aノード影響領域の面積が bノード影響領域の面積より小さい場合 aより小さいとみなす。

    注: sources属性は影響領域の大きい順の レイアウトシフト要素情報を公開し、 レイアウトシフトに最も寄与した要素が最初となる。

  4. 各Cのメンバーごとに 帰属を作成するアルゴリズムを実行して作成した FrozenArray 形式のLayoutShiftAttribution オブジェクト群を返す。

Node Nに対し 帰属を作成する と求められた時、次を行う:
  1. N関連 Realm で 新しいLayoutShiftAttribution オブジェクトAを作成する。

  2. A対応ノードNを設定する。

  3. ApreviousRect 属性に、 長方形(Rectangle)のうち 前フレームの視覚的表現 全体を含む最小のものをCSSピクセル 単位で設定する。

  4. AcurrentRect 属性に、 長方形(Rectangle)のうち 視覚的表現 全体を含む最小のものをCSSピクセル 単位で設定する。

  5. Aを返す。

6. セキュリティとプライバシーに関する考慮事項

レイアウト不安定性は リソースタイミング と間接的な関係を持つ。なぜなら遅いリソースがなければ発生しなかった一時的なレイアウトを引き起こす可能性もあるためだ。 リソースタイミング情報は、統計的フィンガープリント 目的で悪意あるウェブサイトに利用されうる。レイアウト不安定性APIは 現在のブラウジングコンテキストの不安定性のみを報告する。複数のブラウジングコンテキストをまたいだ 不安定性スコアの集約情報は直接提供しない。開発者は手動でこのような集約を実装できるが、 オリジンの異なる オリジンの ブラウジングコンテキスト同士は、不安定性スコアを共有するには協調が必要となる。

適合性

文書の慣例

適合性要件は、説明的な記述と RFC 2119 用語の組み合わせで表現される。 規範的部分で使われる “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL” などのキーワードは RFC 2119 の解釈に基づく。 ただし可読性のため、この仕様ではそれらは全て大文字では表記されていない。

この仕様の本文は、明示的に非規範的と記載された節、例、およびノートを除き、すべて規範的である。 [RFC2119]

この仕様の例は “for example” と導入されるか、または class="example" により規範的本文と分離される。例:

これは情報例(インフォマティブな例)です。

インフォマティブノートは “Note” で始まり、 class="note" によって規範的本文から分離される。次のようになる:

注記:これは情報ノートです。

索引

本仕様で定義される用語

参照で定義される用語

参考文献

規範的参照

[CSS-BREAK-4]
Rossen Atanassov; Elika Etemad. CSS Fragmentation Module Level 4. URL: https://drafts.csswg.org/css-break-4/
[CSS-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 5. URL: https://drafts.csswg.org/css-cascade-5/
[CSS-COLOR-3]
Tantek Çelik; Chris Lilley; David Baron. CSS Color Module Level 3. URL: https://drafts.csswg.org/css-color-3/
[CSS-DISPLAY-4]
Elika Etemad; Tab Atkins Jr.. CSS Display Module Level 4. URL: https://drafts.csswg.org/css-display-4/
[CSS-OVERFLOW-3]
Elika Etemad; Florian Rivoal. CSS Overflow Module Level 3. URL: https://drafts.csswg.org/css-overflow-3/
[CSS-TEXT-3]
Elika Etemad; Koji Ishii; Florian Rivoal. CSS Text Module Level 3. URL: https://drafts.csswg.org/css-text-3/
[CSS-TRANSFORMS-1]
Simon Fraser; et al. CSS Transforms Module Level 1. URL: https://drafts.csswg.org/css-transforms/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. URL: https://drafts.csswg.org/css-values-4/
[CSS-WRITING-MODES-4]
Elika Etemad; Koji Ishii. CSS Writing Modes Level 4. URL: https://drafts.csswg.org/css-writing-modes-4/
[CSS2]
Bert Bos; et al. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. URL: https://drafts.csswg.org/css2/
[CSSOM-VIEW-1]
Simon Fraser; Emilio Cobos Álvarez. CSSOM View Module. URL: https://drafts.csswg.org/cssom-view/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[ELEMENT-TIMING]
Element Timing API. Editor's Draft. URL: https://w3c.github.io/element-timing/
[GEOMETRY-1]
Simon Pieters; Chris Harrelson. Geometry Interfaces Module Level 1. URL: https://drafts.fxtf.org/geometry/
[HR-TIME-2]
Ilya Grigorik. High Resolution Time Level 2. URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[PAINT-TIMING]
Ian Clelland; Noam Rosenthal. Paint Timing. URL: https://w3c.github.io/paint-timing/
[PERFORMANCE-TIMELINE]
Nicolas Pena Moreno. Performance Timeline. URL: https://w3c.github.io/performance-timeline/
[RESOURCE-TIMING]
Yoav Weiss; Noam Rosenthal. Resource Timing. URL: https://w3c.github.io/resource-timing/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[VISUAL-VIEWPORT]
Visual Viewport API. cg-draft. URL: https://wicg.github.io/visual-viewport/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

参考(informative)参照

[INTERSECTION-OBSERVER]
Stefan Zager; Emilio Cobos Álvarez; Traian Captan. Intersection Observer. URL: https://w3c.github.io/IntersectionObserver/
[RESIZE-OBSERVER-1]
Aleks Totic; Greg Whitworth. Resize Observer. URL: https://drafts.csswg.org/resize-observer/

IDL 索引

[Exposed=Window]
interface LayoutShift : PerformanceEntry {
  readonly attribute double value;
  readonly attribute boolean hadRecentInput;
  readonly attribute DOMHighResTimeStamp lastInputTime;
  readonly attribute FrozenArray<LayoutShiftAttribution> sources;
  [Default] object toJSON();
};

[Exposed=Window]
interface LayoutShiftAttribution {
  readonly attribute Node? node;
  readonly attribute DOMRectReadOnly previousRect;
  readonly attribute DOMRectReadOnly currentRect;
};

課題索引

要素取得(get an element)アルゴリズムは Element Timing 仕様から本仕様で再利用しやすい場所に移すべきである。
要素取得(get an element)アルゴリズムは Node ではなく Element を受け付けるよう 一般化すべきである。