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 |
適用対象: | 下記参照 |
継承: | no |
パーセンテージ: | n/a |
算出値: | キーワードnone、またはsize、layout、paintのいずれか一つ以上 |
標準順序: | 文法による |
アニメーション型: | アニメーション不可 |
ユーザーエージェントは、非視覚メディアを含むすべてのメディアでこのプロパティをサポートすることが期待されています。
containプロパティは、著者が要素およびその内容が、できる限り他のドキュメントツリーから独立していることを示すことを可能にします。 適切にcontainを使用してページを描画することで、ユーザーエージェントはより強力な最適化を利用でき、些細な変更によって意図せず遅いコードパスに陥る心配がなくなります。
- none
- この値は、プロパティに効果がないことを示します。 要素は通常通り描画され、コンテインメント効果は適用されません。
- strict
- この値はsize layout paint styleに算出され、要素に対してすべてのコンテインメントを有効化します。
- content
-
この値はlayout paint styleに算出され、要素に対してコンテインメントのうち、size containmentを除くすべてを有効化します。
注: contain: contentは広範囲に適用しても比較的「安全」であり、実際には効果も控えめなので、ほとんどのコンテンツはその制約に引っかかることはありません。 ただし、size containmentが有効にならないため、要素は依然として内容のサイズに応答でき、これが望まれる以上にレイアウトの無効化がツリーの上位まで波及することがあります。 可能な限りcontain: strictを使用して、最大限のコンテインメントを得てください。
- size
- この値は、要素にsize containmentを有効化します。 これにより、containment boxのレイアウト時に子孫を調べる必要がなくなります。
- layout
- この値は、要素にlayout containmentを有効化します。 これにより、containment boxはレイアウト上完全に不透明となり、外部の影響を受けず、内部レイアウトも外部に影響しません。
- style
- この値は、要素にstyle containmentを有効化します。 これにより、要素やその子孫だけでなく、他の要素にも影響する可能性があるプロパティについて、その効果が要素外に漏れないようになります。
- paint
- この値は、要素にpaint containmentを有効化します。 これにより、containment boxの子孫がその範囲外に表示されなくなり、要素が画面外や非表示の場合、その子孫も確実に非表示となります。
このプロパティは原則としてすべての要素(CSS Pseudo-Elements 4
§ 4.1 生成コンテンツ疑似要素: ::beforeおよび::afterを含む)に適用されますが、コンテインメントの種類によっては一部の要素に効果がありません。詳細は§ 3 コンテインメントの種類を参照してください。
また、[SVG2]の場合、containプロパティは、CSSレイアウトボックスを持つsvg
要素にのみ適用されます。
例えば、マイクロポスト型SNSが以下のようなマークアップだったと仮定します:
< body >
< aside > ...</ aside >
< section >
< h2 > Messages</ h2 >
< article >
Lol, check out this dog: images.example.com/jsK3jkl
</ article >
< article >
I had a ham sandwich today. #goodtimes
</ article >
< article >
I have political opinions that you need to hear!
</ 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 Fragmentation 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ツリーのどの部分が「汚れて」再レイアウトが必要かの計算をコンテインメントボックスで止めることができます。
-
ページのレイアウト時に、コンテインメントボックスが画面外や隠れている場合、 その内容のレイアウト(「通常レイアウト」)を遅延したり、低い優先度で処理できます。
3.2. レイアウト・コンテインメント
要素にレイアウト・コンテインメントを与えると、その主ボックスが レイアウト・コンテインメントボックスとなり、以下の効果を持ちます:
-
あるフラグメンテーションコンテナが属するフラグメンテーションコンテキスト内に、少なくとも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 Fragmentation 3 § 3.1 ボックス間の改ページ: break-beforeとbreak-afterプロパティで説明されているような親への伝播は行われません。
注: これにより、強制改ページがボックスとそのコンテナの間に発生する可能性が生まれます(CSS Fragmentation 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で削除されていたスタイル・コンテインメントを復元しました。