1. はじめに
ウェブサイトを効率的に描画するためには、ユーザーエージェントがページのどの部分が表示されているか、どの部分が現在表示されているセクションに影響を与える可能性があるか、そしてどの部分を無視できるかを検出できることが重要です。
サブツリーがページ全体から何らかの方法で独立しているかどうかを推測するための様々なヒューリスティックがありますが、これらは脆弱であり、ページへの些細な変更によって意図せずヒューリスティックテストに失敗し、描画が遅いコードパスに陥ることがあります。また、ヒューリスティックでは検出が困難または不可能な、隔離した方が良いものも多く存在します。
これらの問題を解消し、サブツリーをページの他の部分から強力かつ予測可能に分離できるようにするため、本仕様ではcontainプロパティを定義します。
さらに、画面外のコンテンツの最適化を促進するため、本仕様ではcontent-visibilityプロパティも定義し、必要ない場合は要素のレイアウトや描画を完全にスキップできるようにします。
1.1. モジュール間の相互作用
本書は、従来の仕様には存在しなかった新しい機能を定義しています。加えて、安定すれば[CSS-CONTAIN-1]を置き換え、上書きすることを目指しています。
1.2. 値の定義
本仕様は、CSS2のプロパティ定義規則([CSS2])を、CSS-VALUES-3の値定義構文([CSS-VALUES-3])を用いて踏襲しています。 この仕様で定義されていない値型はCSS Values & Units [CSS-VALUES-3]で定義されています。 他のCSSモジュールとの組み合わせにより、これらの値型の定義が拡張される場合があります。
各プロパティ定義に記載された値に加え、本仕様で定義される全プロパティはCSS全体で使えるキーワードも値として受け付けます。 可読性のため、ここでは明示的に繰り返していません。
2. 強力なコンテインメント: containプロパティ
| 名前: | contain |
|---|---|
| 値: | none | strict | content | [ size || layout || style || paint ] |
| 初期値: | none |
| 適用対象: | 以下を参照 |
| 継承: | しない |
| パーセンテージ: | 該当なし |
| 算出値: | キーワード none、または size、layout、paint の1つ以上 |
| 正規順序: | 文法に従う |
| アニメーション型: | アニメーション不可 |
ユーザーエージェントは、非視覚的なものを含むすべてのメディアでこのプロパティをサポートすることが期待される。
contain プロパティにより、作者は要素とその 内容が、可能な限り文書ツリーの残りの部分から独立していることを示すことができる。 これにより、ユーザーエージェントは contain を適切に使用してページをレンダリングする際に、はるかに強力な最適化を利用でき、 また、無害な変更によってページが誤って低速なコードパスに入ることがないと 作者が確信できるようになる。
- none
- この値は、プロパティが何の効果も持たないことを示す。 要素は通常どおりレンダリングされ、 封じ込めの効果は適用されない。
- strict
- この値は size layout paint style に算出され、 したがって要素に対してすべての形式の封じ込めを有効にする。
- content
-
この値は layout paint style に算出され、
したがって要素に対してサイズ封じ込めを除くすべての形式の封じ込めを有効にする。
注: contain: content は広範囲に適用しても比較的「安全」である。 実際にはその効果はかなり限定的であり、 ほとんどのコンテンツはその制約に抵触しない。 ただし、サイズ封じ込めは適用されないため、 要素は依然としてその内容のサイズに応答でき、 これによりレイアウトの無効化が望ましい範囲よりもさらに上位のツリーまで伝播する可能性がある。 可能な場合は contain: strict を使用し、 可能な限り多くの封じ込めを得ること。
- size
- この値は、要素に対してサイズ 封じ込めを有効にする。 これにより、封じ込めボックスは、その子孫を調べる必要なく レイアウトできることが保証される。
- layout
- この値は、要素に対してレイアウト封じ込めを有効にする。 これにより、封じ込めボックスはレイアウト上完全に不透明であることが保証される。 外部の何ものもその内部レイアウトに影響を与えることができず、 その逆も同様である。
- style
- この値は、要素に対してスタイル封じ込めを有効にする。 これにより、 要素とその子孫だけでなく、それ以上の範囲に効果を及ぼし得るプロパティについて、 その効果が要素の外に漏れ出さないことが保証される。
- paint
- この値は、要素に対して描画封じ込めを有効にする。 これにより、封じ込めボックスの子孫がその境界の外側に表示されないことが保証され、 したがって、要素が画面外にあるか、その他の理由で見えない場合、 その子孫も見えないことが保証される。
このプロパティは一般にすべての要素(CSS 疑似要素 4
§ 4.1 生成コンテンツ疑似要素: ::before および ::afterを含む)に適用されるが、
一部の種類の封じ込めは、一部の要素では効果を持たない。
詳細は § 3 封じ込めの種類を参照。
さらに、[SVG2] の場合、
contain プロパティは、関連付けられた CSS レイアウトボックスを持つ
svg
要素にのみ適用される。
たとえば、マイクロ投稿型ソーシャルネットワークに次のようなマークアップがあるとする:
< body >
< aside > ...</ aside >
< section >
< h2 > メッセージ</ h2 >
< article >
笑、この犬見てよ: images.example.com/jsK3jkl
</ article >
< article >
今日はハムサンドを食べた。#goodtimes
</ article >
< article >
君に聞いてもらいたい政治的な意見がある!
</ article >
…
</ section >
</ body >
サイトにはおそらく非常に多くのメッセージが表示されるが、 それぞれは独立しており、サイト上の他のものには影響しない。 したがって、これをユーザーエージェントに伝えるため、それぞれに contain: content を指定でき、 これによりページを最適化し、画面外にあるメッセージについて多くの計算を省略できる。 各メッセージのサイズが事前に分かっている場合は、contain: strict を適用して、さらなる 制約を伝えることができる。
さらに、HTML の
html
または
body
要素のいずれかで、何らかの封じ込めが
有効になっている場合、
body
要素から
初期包含ブロック、ビューポート、またはキャンバス背景への
プロパティの伝播は無効になる。
特に、これは次のものに影響する:
-
writing-mode、direction、および text-orientation(CSS Writing Modes 3 § 8 主要書字モードを参照)
-
overflow とそのロングハンド(CSS Overflow 3 § 3.5 オーバーフローのビューポートへの伝播を参照)
-
background とそのロングハンド(CSS Backgrounds 3 § 2.11.2 キャンバス背景と HTML <body> 要素を参照)
注:
html
要素自体に設定されたプロパティの、
初期包含ブロック、ビューポート、またはキャンバス背景への
伝播には影響しない。
注: contain 以外のいくつかのプロパティも、 要素に対してさまざまな封じ込めを有効にできる。 これらは contain の値には影響しない。 たとえば、要素が contain: none であっても、レイアウト封じ込めが content-visibility によって 有効になっている場合がある。
3. コンテインメントの種類
要素にはいくつかの種類のコンテインメントがあり、その子孫がページ全体に及ぼす影響を様々な方法で制限します。コンテインメントは、ユーザーエージェントによるより強力な最適化を可能にし、 著者がページを機能単位として構成するのに役立ちます。これは、特定の変更が文書全体にどれだけ広く影響するかを制限するためです。
新しいプロパティやメカニズムを導入する仕様の著者は、様々な種類のコンテインメントが導入対象にどのように影響するかを考慮し、ここで説明されていない効果がある場合は仕様に記載する必要があります。
3.1. サイズ・コンテインメント
要素に サイズ 封じ込めを指定すると、その主要ボックスは サイズ封じ込め ボックスとなり、次の効果を持つ:
-
サイズ封じ込めボックスの内在サイズは、 要素にコンテンツがないものとして決定され、 空であるかのようにサイズを決定する場合と同じロジックに従う。
注: これは、min-content または max-content キーワードの明示的な使用だけでなく、 これらの測定値に依存するあらゆる計算にも影響する。 たとえば、サイズ封じ込めされたアイテムが配置されるグリッド トラックのサイズ決定や、 封じ込めボックスの親にfit-content サイズ決定を行う場合などである。
-
サイズ封じ込めボックスとそのコンテンツのレイアウトは、概念的には 2つの段階で行われる:
- 空であるかのように サイズを決定する
-
使用 width および height の封じ込めボックスに対する値は、
ボックスの通常のレイアウトを行う場合と同様に決定されるが、
コンテンツを一切持たないものとして扱われる。
::before、::after、または ::marker などの疑似要素を介したコンテンツさえ存在しないものとする。 置換要素は、自然幅と高さが 0 であり、 自然アスペクト比を持たないものとして扱わなければならない。
注: サイズ封じ込めが抑制するのは自然アスペクト比のみであるため、 その優先アスペクト比に直接影響する aspect-ratio のようなプロパティは 尊重される。
サイズ封じ込めボックスのすべての CSS プロパティは、 通常のレイアウトを行う場合と同様に考慮される。 他の仕様では、特定の例外を定めることができる。
注: 要素のサイズ決定プロパティが内在サイズを指定している場合でも、 それによって必ずしも要素のサイズがゼロになるわけではない: 要素自体に設定されたプロパティは 引き続き考慮され、 その結果、より大きくなる場合がある。
- その場で レイアウトする
- その後、封じ込めボックスのコンテンツ (あらゆる疑似要素を含む)は、 この時点で固定サイズとなった封じ込めボックス内に通常どおりレイアウトされなければならない。
注: サイズ封じ込めはベースライン整列を抑制しない。 それについてはレイアウト 封じ込めを参照。
-
サイズ 封じ込めボックスは一体不可分である(CSS 断片化 3 § 4.1 分断可能点を参照)。
img{ width : 100 px ; aspect-ratio : 1 /1 ; contain : size; } < img src = "https://www.example.com/300x100.jpg" >
aspect-ratio プロパティが宣言されていなかった場合、 画像は 100px × 0px になっていた。 これは、その自然アスペクト比が抑制され、 自然高さが 0 として扱われるためである。
ただし、次のいずれかが真である場合、要素にサイズ封じ込めを指定しても効果はない:
-
要素が主要 ボックスを生成しない場合(display: contents または display: none の場合など)
-
その主要ボックスが 内部テーブルボックスである場合
注: 内部テーブルボックスは、 テーブルキャプションを含まず、 対象外である。 これは、テーブルレイアウトアルゴリズムでは、 ボックスをフロー内コンテンツより小さくすることができないためである。 テーブルセルを空であるかのようにサイズ決定し、その後、サイズを変更せずにその内部へコンテンツをレイアウトすることは、 実質的に未定義の操作である。 width または height プロパティを手動で 0 に設定しても、 そのコンテンツより小さくすることはできない。 この問題はテーブルキャプションには当てはまらず、 テーブルキャプションはコンテンツから独立した固定サイズを持つことが 完全に可能である。
3.1.1. サイズ・コンテインメントによる最適化例
このセクションは規範的ではありません。
サイズ 封じ込めは、それ自体では最適化の機会をあまり提供しない。 それ自体の主な利点は、封じ込めボックスの内容を 封じ込めボックスのサイズに基づいてレイアウトしたいツール (「コンテナークエリー」の概念を実装する JS ライブラリなど)が、 「無限ループ」を恐れることなくそうできることである。 これは、子のサイズを封じ込め ボックスのサイズに応じて変化させることで、封じ込めボックスのサイズも 変化し、 それによって子自身のサイズ決定方法にさらに変化が生じ、 その結果、封じ込めボックスの サイズにもさらに変化が生じる可能性があり、 それが無限に続くような場合である。
ただし、レイアウト 封じ込めと組み合わせると、 有効にできる最適化には、次のものが含まれる(ただし、これらに限定されない):
-
封じ込めボックスの子孫のスタイルまたは内容が変更された場合、 DOM ツリーのどの部分が「dirty」になり、再レイアウトが必要になる可能性があるかを計算する処理は、 封じ込めボックスで停止できる。
-
ページをレイアウトする際、 封じ込めボックスが画面外にあるか、隠されている場合、 その内容のレイアウト(すなわち「その場でレイアウトする」こと)は、遅延させるか、より低い 優先度で実行できる。
3.2. レイアウト・コンテインメント
要素に レイアウト 封じ込めを指定すると、その主要ボックスは レイアウト封じ込め ボックスとなり、次の効果を持つ:
-
断片化コンテキストの少なくとも1つの断片化コンテナーがレイアウト封じ込めを持つ場合、 または、断片化コンテキストの少なくとも1つの断片化コンテナーがレイアウト 封じ込めボックスの子孫であり、かつ同じ断片化コンテキストの後続する少なくとも1つの断片化コンテナーが、レイアウト封じ込めを持つその 同じ要素の子孫ではない場合、 自身が断片化コンテナーであるか、 または断片化コンテナーの祖先である最初のレイアウト封じ込めボックスは、断片化フローの残りを「閉じ込め」なければならない:断片化はレイアウト封じ込め境界を越えて続いてはならず、 最初のレイアウト封じ込め境界内の最後の断片化コンテナーは、その断片化コンテキスト内の最後の断片化 コンテナーであるかのように扱われる。
断片化コンテキスト内の後続する断片化コンテナーが、断片化フロー内にさらに コンテンツが残っている場合にのみ生成されるなら、 それらは生成されない。 それらがいずれにせよ存在する場合は、 断片化コンテキストの一部として残るが、 断片化フローからコンテンツを一切受け取らない。
注: 執筆時点では、この点の 影響を受ける安定した仕様は存在しない。 断片化コンテキストの一部(すべてではない)の断片化コンテナーを レイアウト封じ込めできる(またはレイアウト封じ込めされた要素の子孫にできる) 仕様のみが対象となる。 [CSS-PAGE-3]にも [CSS-MULTICOL-1]にも当てはまらない。 それでもこの要件が含まれているのは、 これを可能にするいくつかの仕組みが検討されてきたためであり (例:[CSS-REGIONS-1]、::nth-fragment()、マルチカラムの 個々のカラムを対象とする仮想的なセレクターなど…)、 そのような仕組みがこの規則に従わなければ、レイアウト封じ込めが提供することを意図した保証が 実現されないためである。[CSS-REGIONS-1]には、レイアウト 封じ込めが リージョンにどのような影響を与えるかについて詳細が記載されている。
< article > Lorem ipsum…</ article > < div id = a ></ div > < aside > < div id = b ></ div > < div id = c ></ div > </ aside > < aside > < div id = d ></ div > < div id = e ></ div > </ aside > < div id = f ></ div > article{ flow-into : foo;} #a, #b, #c, #d, #e, #f{ flow-from : foo;} aside{ contain : layout} この [CSS-REGIONS-1] の例では、 コンテンツは
#aから#bへ、#bから#cへ流れることができる。 しかし、#cは最初のレイアウト封じ込め ボックス内の最後の断片化コンテナーであるため、残りのすべてのコンテンツを閉じ込め、#d、#e、#fのいずれにも何も流れ込まない。 -
overflow プロパティの算出値が visible、clip、またはそれらの組み合わせのいずれかである場合、 すべてのオーバーフローはインク オーバーフローとして扱わなければならない。
-
レイアウト封じ込めボックスはスタッキングコンテキストを生成する。
-
強制分断はレイアウト封じ込めボックス内では 許可されるが、CSS 断片化 3 § 3.1 ボックス間の分断:break-before および break-after プロパティで別途説明されているように親へ伝播することはない。
注: これにより、以前は存在しなかった、 ボックスとそのコンテナーの間で強制分断が発生し得る可能性が導入される(CSS 断片化 3 § 4.1 分断可能点を参照)。
-
vertical-align プロパティ、 またはその効果において レイアウト封じ込めボックスのベースラインの位置を その子孫以外の何かに関連付ける必要があるその他のプロパティの目的上、 封じ込めボックスは ベースラインを持たないものとして扱われる。
ただし、次のいずれかが真である場合、要素にレイアウト封じ込めを指定しても効果はない:
-
要素が主要 ボックスを生成しない場合(display: contents または display: none の場合など)
-
その主要ボックスが、 table-cell 以外の内部テーブルボックスである場合
3.2.1. レイアウト・コンテインメントによる最適化例
このセクションは規範的ではありません。
レイアウト・コンテインメントによって可能になる最適化例(代表例、限定されない):
-
ページのレイアウト時、 別々のコンテインメントボックスの内容は互いに影響しないことが保証されているため、並列でレイアウトできます。
-
ページのレイアウト時、 コンテインメントボックスが画面外や隠れていて、画面の可視部分のレイアウトがそのコンテインメントボックスのサイズに依存しない場合(例えば、コンテインメントボックスがブロックコンテナの末尾にあり、ユーザーがブロックコンテナの先頭を表示している場合)、 コンテインメントボックスの内容のレイアウトを遅延したり、優先度を下げて処理できます。
(サイズ・コンテインメントと組み合わせることで、この最適化をより自由に適用できます。)
3.3. スタイル・コンテインメント
要素にスタイル・コンテインメントを与えると、以下の効果があります:
-
counter-incrementおよびcounter-setプロパティは要素のサブツリーにスコープされ、新しいカウンターを作成します。
-
contentプロパティのopen-quote、close-quote、no-open-quoteおよびno-close-quoteの効果は要素のサブツリーにスコープされます。
注:これはサブツリー内の引用ネストの深さが、通常のコンテキストから開始されて変更されないことを意味しますが、サブツリー内でこれらの値による深さの変更はサブツリー外の引用ネストの深さに影響しません。
注: [CSS-REGIONS-1]にはスタイル・コンテインメントがリージョンに与える規範的要件が定められています。
スコープされたプロパティは、特定の要素またはサブツリーに効果範囲が限定されます。
-
要素にスコープされている場合、プロパティの効果評価において、スコープする要素が文書のルートであるかのように振る舞わなければなりません。 スコープ外でのプロパティの使用は、スコープ要素内外のプロパティの使用に影響しないものとし、逆も同様です。
注:「要素へのスコープ」は現在未使用です。将来の仕様で拡張ポイントとして定義されています。
-
サブツリーにスコープされた場合も同様ですが、スコープ要素自体はツリー外(文書全体と同様)とみなされ、その要素へのプロパティの効果はスコープの影響を受けません。 サブツリー内の要素に対してスコープされたプロパティの効果を考慮する際、サブツリーの基底要素が文書のルートであるかのように扱います。
1 1.2と表示される:
< div ></ div >
div {
contain : style;
counter-increment : n;
}
div::before, div::after {
content : counters ( n, '.' ) " " ;
}
div::after {
counter-increment : n 2 ;
}
3.3.1. スタイル・コンテインメントによる最適化例
このセクションは規範的ではありません。
スタイル・コンテインメントによって可能になる最適化例(代表例、限定されない):
-
スタイル・コンテインメントを持つ要素の子孫プロパティが変更された場合、DOMツリーのどの部分が「汚れて」スタイル再計算が必要かの計算をスタイル・コンテインメント要素で止めることができます。
3.4. ペイント・コンテインメント
要素にペイント・コンテインメントを与えると、その主ボックスが ペイント・コンテインメントボックスとなり、以下の効果を持ちます:
-
要素の内容(インクやスクロール可能なオーバーフローも含む)は、ペイント・コンテインメントボックスのオーバーフロークリップエッジでクリップされなければなりません。 この際、[[css-backgrounds-3#corner clipping|角のクリッピング]]も考慮します。 クリップされた内容へのアクセスや存在を示す仕組みの作成は含みませんし、他のプロパティ(例えば overflow、resize、text-overflowなど)によるそのような仕組みの作成も妨げません。
注:このクリッピング形状はoverflow-clip-marginを考慮し、 ペイント・コンテインメントを持つ要素でも少しは通常の境界を超えて描画できます。
注:この段落で記述されている動作は、使用値時にoverflow-x: visibleをoverflow-x: clipに、 overflow-y: visibleをoverflow-y: clipに変えることと同等ですが、 overflow-xやoverflow-yの他の値は変更しません。
-
ペイント・コンテインメントボックスは 絶対位置決めコンテインメントブロックと 固定位置決めコンテインメントブロックを確立します。
-
ペイント・コンテインメントボックスはスタッキングコンテキストを生成します。
ただし、以下のいずれかが当てはまる場合、要素にペイント・コンテインメントを与えても効果はありません:
-
要素が主ボックスを生成しない場合(display: contentsやdisplay: noneの場合など)
-
主ボックスが 内部テーブルボックス(ただしtable-cellは除く)の場合
3.4.1. ペイント・コンテインメントによる最適化例
このセクションは規範的ではありません。
ペイント・コンテインメントによって可能になる最適化例(代表例、限定されない):
-
コンテインメントボックスが画面外または隠れている場合、 UAは通常、その内容の描画を省略できます。 内容も確実に画面外・不可視となるためです。
注: blur()フィルター([FILTER-EFFECTS-1])など一部のペイント効果は局所的でない影響を持ちます。 ユーザーエージェントはこれらを管理する必要があり、 該当フィルターが使われている場合、子孫が変更されると一部領域の再描画が必要になることがあります。 たとえペイント・コンテインメントが有効で通常スキップできる場合でもです。
-
クリップされたコンテンツが overflow、resize、 text-overflowなどの別の仕組みでアクセス可能でない限り、 UAはボックスのサイズぴったりに「キャンバス」領域を確保できます。 (スクロール可能な状況、例:overflow: hiddenなどでは、 クリップされたコンテンツへスクロールできるため、 UAは予測的に多少余分に描画しておき、スクロール直後にすぐ表示されるようにします。 これは1フレーム遅れて表示されるのを防ぐためです。)
-
スタッキングコンテキストであることが保証されるため、 スクロール要素は単一のGPUレイヤーとして描画できます。
4. 要素の内容を完全に抑制する: content-visibilityプロパティ
| Name: | content-visibility |
|---|---|
| 値: | visible | auto | hidden |
| 初期値: | visible |
| 適用対象: | レイアウト・コンテインメントが適用可能な要素 |
| 継承: | no |
| パーセンテージ: | n/a |
| 算出値: | 指定通り |
| 正規順序: | 文法通り |
| アニメーション型: | アニメーション不可 |
content-visibilityプロパティは、 要素がその内容を描画するかどうかを制御します。 また、強力なコンテインメントを強制し、 ユーザーエージェントが必要になるまで大規模なレイアウトやレンダリング作業を省略できるようにします。 以下の値があります:
- visible
-
効果なし。 要素の内容は通常通りレイアウト・描画されます。
- hidden
-
要素は内容をスキップします。
スキップされた内容はユーザーエージェントの機能でアクセス可能であってはなりません (ページ内検索、タブ移動、選択・フォーカスなど)。
注: これは内容にdisplay: noneを与えるのに類似しています。
- auto
-
レイアウト・コンテインメント、 スタイル・コンテインメント、 ペイント・コンテインメントを有効化します。
とは異なり、 スキップされた内容は ページ内検索、タブ移動などのユーザーエージェント機能で通常通り利用可能であり、 フォーカス・選択も可能です。
ユーザーエージェントはスキップされた内容について、 できる限りレイアウト・レンダリング作業を省略すべきです。 強力なコンテインメントと内容の不可視・非操作化の組み合わせにより、大幅な最適化が可能となります。 何らかのレンダリング作業が行われた場合、 ユーザーエージェントは可能なら以前のレイアウト状態を保持し、 スキップされた内容を後で素早く表示できるようにすべきです。
-
レイアウト・コンテインメントにより、 スキップされたサブツリーのレイアウト作業を省略可能です。 これらのレイアウト結果はコンテナ要素外に影響しないためです。
-
スタイル・コンテインメントにより、 スキップされたサブツリーのカウンター処理を省略可能です。 カウンターはコンテナ要素外に影響しないためです。
-
ペイント・コンテインメントにより、 ペイント内容のインクオーバーフローがクリップされます。 これにより、要素の可視部分がビューポートに近づいたとき(content-visibility: autoの場合は描画開始)、 ユーザーエージェントが確実に判断できます。
-
サイズ・コンテインメントにより、 スキップされたサブツリーのレイアウトを省略可能です。 これらのレイアウト結果はコンテナ要素のサイズに影響しません。
content-visibility: auto の場合、レイアウト 封じ込め、スタイル封じ込め、および 描画封じ込めは、要素がスキップされていない場合でも維持されることに注意すること。 これは、要素がスキップ状態になったり、その状態から抜けたりする結果として生じる封じ込めの変更によって発生する レイアウト変更を防ぐためである。
-
要素が「画面上にある」場合: ペイント・コンテインメントボックスのoverflow clip edgeがビューポート、またはUA定義のビューポート周辺マージンと交差する。
注: このマージンは、UAが要素を間もなくビューポート内に表示予定として準備できるよう設けます。 デフォルト値50%が妥当な目安です。
-
要素またはその内容が選択されている場合 (CSS Pseudo-Elements 4 § 3 ハイライト疑似要素参照)。
4.1. content-visibility: hiddenの使用
このセクションは規範的ではありません。
content-visibility: hiddenは要素に強力な制約を課すため、慎重に使用する必要があります。 一方で非常に便利なシナリオも可能となり、既存技法より優れる場面も多く、以下にいくつか例を示します。
-
ページで描画しない要素やテキストの計測が必要な場合、一般的には計測対象を画面外に配置し、 position: absolute; left: -100000px;のように設定し、
getBoundingClientRect()などのAPIを呼び出します。しかし、ページがこの内容を表示する意図がなくても、ユーザーエージェントは万が一画面表示に影響する可能性を考慮し、スタイリング・レイアウト・レンダリングを完全に行います。 また、追加の工夫なしでは、内容が意図せず画面に表示されてしまうことも完全には防げません。 極端なleft値(上記のような)でも、内容によっては不十分な場合があります。
この内容をcontent-visibility: hiddenのラッパーで囲むことで、これらの問題は全て解決できます。 ラッパーに境界線や背景などがなければ、その要素およびスキップされた内容は、どれだけ大きくなっても絶対に画面に描画されません。 スキップされるため、ユーザーエージェントは必要になるまでスタイリングやレイアウト処理を行わず、スクリプトで要求された時にだけ処理します。
-
「シングルページアプリ」は複数の独立したペインや「ビュー」から構成され、同時に表示されるのは一つだけという場合が多いです。
非表示のビューについて、スタイリング・レイアウト・レンダリングなどのコストを避けたい場合、完全に文書から削除するか、最低でもdisplay:noneを適用します。 しかし、ビューの表示が必要になった際には、全てのスタイリング・レイアウト・レンダリングなどを一度に行う必要があり、表示までに遅延が発生する場合があります。
代わりにビューを画面外に配置すれば、すぐに使える状態にできますが、非表示時でも常にスタイリング・レイアウト・レンダリングのコストがかかり、非表示ビューが多数ある場合は負担が大きくなります。 また、スクリーンリーダーやCtrl-Fによる検索など、アクセシビリティツールにも表示されてしまい、ユーザーを混乱させる恐れがあります。
content-visibility: hiddenはこれら両方を改善します。 スキップされている間はユーザーエージェントが処理を行わず、 スクリーンリーダー、ページ内検索などにも表示されません。 さらに、以前表示されていた場合は、そのスタイリング・レイアウト状態が保持されるため、再表示も高速です。
-
要素を「不可視」にしたいが、レイアウト上はページに残したい場合、 visibility: hiddenを使うのが一般的です。 しかし、visibility: hiddenな要素の子孫は、 visibility: visibleを指定することで再び表示されてしまうため、直感的でない場合があります。
content-visibility: hiddenは類似の目的を達成できますが、 子孫が「オフ」にして表示を開始することはできず、祖先が解除するまで「隠れた」ままとなります。
さらに、content-visibility: hiddenは多くのコンテインメント値も適用されるため、 常にvisibility: hiddenほど自由に使えるわけではありませんが、 制約が許容できる場合は、より信頼性・一貫性のある要素内容の非表示手段となります。
4.2. content-visibility: autoの使用
このセクションは規範的ではありません。
content-visibility: autoはよりも複雑な値です。 display: noneに似ているわけではなく、 要素内容をユーザーに関連が生じたタイミングで適応的に非表示・表示します。 また、スキップされた内容をユーザーエージェントから隠さないため、 スクリーンリーダーやページ内検索など各種ツールは通常通り操作可能です。
これはcontainmentのアップグレード版として考えるのが最適です。 著者が大量のコンテンツ(長いスクロール可能リスト等)を表示し、多くが画面外となる場合、 そのコンテンツが強力なcontainmentに問題なければ、 content-visibility: autoを用いて全てのcontainmentを一度に適用できます。 これはユーザーエージェントに対し、 内容への処理をスキップしてよい(表示時に少し遅延が発生することもあるが、大量のコンテンツを文書に保持しつつ、ほとんど表示されないことが重要)という強いヒントにもなります。
注: content-visibility: autoは多くの場合、複雑な「バーチャルリスト」技法の代わりに使えます。
content-visibility: autoは要素の内容が全くユーザーに関連しない時のみスキップされるため、適用は適度に細かい粒度で行うのが最適です。
代わりに、content-visibilityを個々のツイートに適用し、 ツイートごとに画面外になった時にスキップできるようにすべきです。
content-visibility: autoは要素が内容をスキップする時にサイズ・コンテインメントを課します。 そのため、要素のサイズが内容に依存している場合は、ページレイアウト(特にスクロールバー位置)が要素の画面外化・スキップによって「ジャンプ」することがあります。
-
要素を固定サイズにする
-
Grid などのレイアウトを慎重に構成し、 内容に依存せずに要素のサイズを決定する
-
contain-intrinsic-size を使用して、要素のサイズの 推定値を設定する
-
contain-intrinsic-size: auto を使用して、 要素が最後にレンダリングされたときの正確なサイズを自動的に「スナップショット」し、 スキップされる前のサイズを利用する(さらに、レンダリングされてサイズを スナップショットできるようになる前に使用するサイズの推定値も提供する)
たとえば、 Twitter では、 平均的なツイートの高さは約 200px であるため、 contain-intrinsic-size: auto 500px 200px により、 前後のツイートがスキップされている場合でも、 スクロールバーのつまみがおおよそ正しいサイズと位置になることが保証される一方で、 ツイートが画面上にある場合には、その内容に応じてサイズを決定できる。 すべてのツイートが少なくとも一度は表示されており (かつ、スキップされている間にサイズが変更されていない)限り、 スキップされている間も、そのサイズは正確に正しくなり、 したがってスクロールバーのつまみも同様に正しくなる。 新たに読み込まれたツイートだけが (たとえば、さらに下へスクロールしている間に タイムラインの上部で読み込まれたもの) 200px という高さの推定値に依存せざるを得ない。
4.3. content-visibility: autoの状態変化検出: contentvisibilityautostatechanged イベント
contentvisibilityautostatechanged
イベントは、content-visibility:
autoスタイルを持つ要素でレンダリング状態が変化し、その要素がユーザーに関連になる/ならなくなった時に発火します。
このイベントは状態変化が発生した時点でタスクを投稿してdispatchされます。
[Exposed =Window ]interface :ContentVisibilityAutoStateChangedEvent Event {(constructor DOMString ,type optional ContentVisibilityAutoStateChangedEventInit = {});eventInitDict readonly attribute boolean skipped ; };dictionary :ContentVisibilityAutoStateChangedEventInit EventInit {boolean skipped =false ; };
ContentVisibilityAutoStateChangedEvent属性の説明:
ContentVisibilityAutoStateChangedEventInitメンバーの説明:
content-visibility: autoサブツリー内の要素は、内容をスキップしている場合でも意味的には有効です。つまり、このシグナルを使ってサブツリー内のDOM更新を無期限にスキップするのは不適切です。むしろ、更新の優先度を下げるために使い、内容が意味的に有効かつ適切に最新状態であることを保証してください。これは、支援技術(アクセシビリティ技術)が祖先が内容をスキップ状態でもこの内容を利用するため、特に重要です。
4.4. 制限事項と補足事項
-
IntersectionObserverの観点では、要素のスキップされた内容は、intersection rootと交差していることはありません。これは、ルート要素とターゲット要素が両方ともスキップされた内容である場合でも同様です。 -
ResizeObserverの観点では、要素のスキップされた内容はサイズが変化しません。これらの要素が後でスキップされなくなった場合、新しいサイズが最後にresize observerへ通知したサイズと異なれば、リサイズ観察が配信されます。 -
要素が内容のスキップを開始または停止する場合、この変更はその変化の効果をレンダリングするフレームのrequestAnimationFrameコールバックが実行された後に発生します。具体的には、こうした変更はProcessing ModelのUpdate the Renderingステップの13番および14番目の手順(「アニメーションフレームコールバックを実行」から「インターセクション監視ステップを実行」まで)の間に有効になります。
要素のビューポート交差判定は内部的なIntersectionObserverバージョンで行うことができます。 ただし、この観察結果はUpdate the Renderingの14番のステップでディスパッチされるため、スキップ(および描画済み)状態への変更は、次のフレーム処理までユーザーに可視化されません。 このため、スキップ状態の更新(containment調整も含む)は、そのフレームに遅延されます。 これにより、たとえばスクリプトが、これら2つのイベント(内部交差観察とスキップ状態更新)の間に要素のcontainment値を取得する場合、現在の描画状態と一致する値を取得でき、強制レイアウトが発生することはありません。 -
content-visibility: autoの可視性の初回判定は、新しいcontent-visibility: auto要素の存在を判定したのと同じフレーム内で行う必要があります。
要素が初めてcontent-visibility: autoを獲得した場合、それが画面上に配置されているかどうかは未定です。状態判定およびその要素がスキップされるかの判定は同じフレーム内で行う必要があります。そうしないと、可視性チェックとスキップ状態の更新が次のフレームに遅延され、要素の位置が空白になる可能性があります。 -
スクロール操作(
scrollIntoView()など)の目的では、content-visibility: autoかつ内容をスキップしている要素は、サイズ・コンテインメントが有効な状態でサイズ・位置が決定されます。注: スクロールして画面内に入ると、その要素は内容スキップ状態でなくなり、サイズ・コンテインメントも解除される場合があります。これにより要素のサイズが変化した場合、ビューポート内で正確な位置合わせができない場合があります。
要素がユーザーエージェントの機能で利用できない場合(例えば、スキップがcontent-visibility: hidden祖先のための場合)、スクロール操作ではその要素には一切スクロールされません。これはレイアウトボックスを持たないものとして扱われます。
-
content-visibility: autoかつ内容をスキップしている要素(またはその内容)がフォーカスされると、ユーザーに関連(よって内容スキップ解除)となり、フォーカス操作によるスクロールの前に状態が切り替わります。
注: そのため、前項と異なり、要素は正しいサイズ・位置でビューポートに整列されます。これは
focus()メソッドの手順順序と一致します。 -
iframeが内容をスキップする場合、または他要素のスキップされた内容の一部である場合、ユーザーエージェントは可能であればiframeのイベントループ内でUpdate The Renderingステップを完全に省略すべきです。注:
iframeが初めてスキップ状態になる瞬間は、少なくとも一度そのステップを実行し、描画出力を削除する必要があります。 -
要素がスキップされている間は、CSSトランジションやアニメーションは更新されません:
-
新しいアニメーションは、スタイルが新たに適用されても生成されません。
-
既存のアニメーションはタイムラインが進みません。
-
要素上で実行中のアニメーションは終了しません。
スクリプトがスキップされた要素のスタイルを問い合わせ(style change eventが発生)、アニメーションやトランジションの状態判定が必要な場合は、そのstyle change event時点のスタイルでサンプリングされます。
CSS Animations 2 § 4 Animation EventsおよびCSS Transitions 2 § 5 Transition Eventsでは、アニメーションやトランジションの更新時にどのオブジェクトが作成され、どのイベントがどのデータで発火するかが定義されています。
要素がスキップされなくなった場合、アニメーションとトランジションはサンプリングされ、その時点から通常通りタイムラインが進行します。
注: 全体として、これはバックグラウンドタブがフォアグラウンドに戻された時のトランジション/アニメーションの挙動に似ています。これにより、ユーザーエージェントは不要なアニメーション処理を極力省略可能で、再度関連性が生じた際にアニメーションを過度に中断しません。
-
-
要素がスキップされている間は、style change eventによって算出スタイルが変化しても、トランジションは開始されません。
要素がスキップされなくなった場合でも、そのスキップ解除に伴うstyle change eventによってトランジションが開始されることはありません。
注: これは、要素がdisplay:noneから非none値に切り替わる場合と似ています。
技術的にはスタイルが(初期値からカスケードによる「本来の」値へ)変化しますが、トランジションは開始されません。 -
要素がcontent-visibility: hiddenな祖先を持ち、トップレイヤーに配置された場合、display: noneのように、ボックスを生成しません。
注: 他の理由(例えばcontent-visibility: autoな祖先のため)でスキップされている場合は、通常通りボックスが生成され、スキップ解除されることもあります。
4.5. アクセシビリティへの影響
ユーザーエージェントが、DOMツリーに似た「アクセシビリティツリー」をスクリーンリーダーなどのアクセシビリティ用途向けに公開する場合 (アクセシビリティAPIにおいて要素の位置やフォーカス可能な要素等を提供)、 スキップされた内容は content-visibility: hidden要素と同様に アクセシビリティツリーでも「スキップ」(除外)されなければなりません。 (display: none要素が文書の全てのビューで除外されるのと同様)
スキップされた内容が content-visibility: auto要素の場合、 ユーザーがページとやり取りする際にアクセシビリティツリー経由か、画面表示経由かを 露呈してはならない。 特に、ユーザーエージェントがcontent-visibility: autoを用いて 画面描画のため画面外コンテンツのレイアウトや描画作業を省略する場合、 アクセシビリティツリー表現のためにも同様に省略すべきです。 これが不可能な場合 (例えば、アクセシビリティツリー上でフォーカス可能要素の正確な位置情報が必要で 周辺も含めた完全なレイアウト処理が必要な場合)、 ユーザーエージェントはスキップされた内容を アクセシビリティツリーから完全に除外しなければなりません。
注: この要件は、アクセシビリティ支援ツール利用者が タイミングチャネルの観測によって特定・プロファイリングされることを防ぐためのものです。 ユーザーエージェントが画面描画で大幅に作業を省略できるのに、 アクセシビリティツリー描画時には全ての作業を行う必要がある場合、 著者はレイアウト操作のタイミングを観察することで ユーザーのページ操作方法を推測できてしまいます。
4.6. 例
< style > . sv { content-visibility : auto ; min-height : 50 px ; } </ style > < div class = sv > ... some content goes here ...</ div >
.sv要素のcontent-visibility: auto値は、 ユーザーエージェントが要素をスキップするかどうか管理できるようにします。 特にこの要素がビューポート付近にある場合、 ユーザーエージェントは描画を開始します。 要素がビューポートから離れると、描画が停止します。 また、要素がスキップされた際は、 ユーザーエージェントはできる限りレンダリング作業を省略するべきです。
< style > . sv { content-visibility : hidden ; } </ style > < div class = sv > ... some content goes here ...</ div >
この場合、要素はビューポートとの交差に関係なくスキップされます。 内容を描画する唯一の方法は、 スクリプトで値を更新してcontent-visibilityを除去または変更することです。 前述同様、ユーザーエージェントは内容のレンダリング作業を極力省略するべきです。
レンダリングの省略がもたらす追加効果として、 内容のレイアウト状態がユーザーエージェントによって保持され、 将来content-visibilityプロパティを削除した場合、 内容のレンダリングが display: noneなどで隠されていた場合よりも高速になります。
< style > body { margin : 0 ; } . sv { content-visibility : hidden ; position : relative ; left : 10 px ; top : 20 px ; } # child { position : relative ; left : 1 px ; top : 2 px ; width : 100 px ; height : 200 px ; } </ style > < div id = target class = sv > < div id = child ></ div > ... some other content goes here ...</ div > < script > ... // UAが以前にレンダリング作業を回避していた場合も、 // この操作でレイアウト等のレンダリング作業が強制される。 target. firstElementChild. getBoundingClientRect(); ... </ script >
前の例と同様、この要素はスキップされます。
ユーザーエージェントはできる限りレンダリング作業を回避すべきです。
しかしこの例では、スクリプトが要素の内容内のレイアウト値にアクセスします。
この状況では、ユーザーエージェントはレンダリング作業を避けられず、
正しい値を返すために以前スキップしていたレンダリングも処理しなければなりません。
この例では、getBoundingClientRect()
の結果は (11, 22) の位置に100x200のサイズとなります。
同じレイアウト値を繰り返し取得しても、追加のレンダリング作業は発生しません。ユーザーエージェントは最後に更新したレンダリング状態を保持すべきです。
また、このようにレンダリング作業が必要となる状況は唯一ではありません。他にもユーザーエージェントがレンダリング作業を回避できない場合があります。
5. プライバシーに関する考慮事項
本仕様の機能による既知のプライバシー上の影響はありません。
6. セキュリティに関する考慮事項
本仕様の機能による既知のセキュリティ上の影響はありません。
他のCSS仕様と同様、本仕様は文書のレンダリングに影響を与えますが、 他のCSSモジュールでも可能だった、あるいは文書整形行為自体に内在する 誤解を招くコンテンツ表示能力を特別に導入するものではありません。
付録A. 変更点
この付録は参考情報です。
2020-12-16作業草案からの変更点
-
スタイル・コンテインメントをstrictおよびcontentキーワードに含めた。
-
overflow clip edgeを border edgeの代わりに 「画面上にあるか」判定に使用(ユーザーに関連の判定の一部)
-
スキップされた要素のアニメーション・トランジションの挙動を定義
-
style containmentの「at risk」マーカーを削除
-
containment有効時、HTML body要素からの伝播を無効化
-
scrollIntoView()がcontent-visibility:hiddenな要素の子にスクロールしないことを明確化
-
content-visibility: hiddenな祖先を持つ要素はトップレイヤーでボックスを生成しないと定義
-
トップレイヤーであることが要素をユーザーに関連付けると定義
-
ペイント効果が非局所的な場合、一部最適化機会が制限されることに言及
-
ContentVisibilityAutoStateChangedイベント追加
2020-06-03作業草案からの変更点
-
containプロパティの算出値の決定方法を変更しました。
-
contain: contentに関する注記の構文エラーを修正しました。
-
用語変更: "containing box" を "containment box" に置き換えました(Level 1の同様の改善と同期)。
-
サイズ・ペイント・コンテインメントに関する編集上の改善と明確化(Level 1の同様の改善と同期)。
-
サイズ・コンテインメントが自然なアスペクト比を抑制することを明示しました(Level 1の同様の改善と同期)。
-
content-visibilityのアニメーション型を「離散」から「アニメーション不可」に変更しました。
-
content-visibility: autoの初回可視性判定タイミングに関する制約を§ 4.4 制限事項と補足事項に追加しました。
2019-11-11作業草案からの変更点
-
ペイント・コンテインメントとoverflow-clip-marginの相互作用を定義しました。
-
content-visibilityプロパティを追加しました。
CSS Containment Level 1からの変更点
-
Level 1で削除されていたスタイル・コンテインメントを復元しました。