
CSS containment(封じ込め)による動的コンテンツのパフォーマンス向上
CSScontainmentは動的コンテンツの内容変更による再レンダリングの負担を軽くする機能です。
開発者がCSSを使って封じ込めする要素をブラウザに指示する事で、再描画パフォーマンスを向上させる効果があります。
封じ込めには通常以外に、描画させない封じ込め、サイズ的な封じ込め、スタイル的な封じ込めなどがあります。
CSS containmentとは
「CSS Containment」は、Chrome52、Opera40のブラウザで新しく実装された機能です(Safariもじきに追随してくる事でしょう)。
この新しく実装されたCSS Containmentはどのような用途に使うのでしょうか。
本記事ではこのCSS Containmentプロパティを詳しく紹介します。
動的コンテンツでパフォーマンスを向上させる
要素が動的に追加または変更されない静的なWEBページの場合は、今回のCSS Containmentを考慮する必要はありません。
ブラウザはページを読み込む際にレイアウトを1回レンダリングするだけで済むからです。
今回は、要素が動的に追加または変更されるWEBページの場合を想定しています。
動的生成箇所は毎回全て再レンダリングされる
動的コンテンツが入る高度なWEBサイトを開発する際、スタイルやレイアウト・レンダリングの量を軽減する事はWEBパフォーマンスにおいて重要です。
例えばテキスト表示等のイベントが発生する度に、生成されたコンテンツ内の情報が変更されるとしましょう。
通常はその生成コンテンツ部分全体が毎回、再度レンダリング範囲に入ります。
ブラウザに再レンダリング部分を指定できる
ブラウザには、構成されたデータのうち「変動しない部分・あるいは自在に変動する部分」の区別がつかないのです。
今まではコンテンツの一部の変更が他の箇所にも影響する事や、どの箇所がその対象内(あるいは対象外)なのかをブラウザに伝える方法がありませんでした。
しかしこのプロパティにより、動的にコンテンツ内に要素を追加する際、ブラウザがレンダリング状態を制御できるのです。
具体的な動的処理ケース
例えば以下の様な連続コンテンツを生成する部分があったとしましょう。
イベント発生前
<div class="view">contents1</div> <div class="view">contents2</div> <div class="view">contents3</div> …以下繰り返し処理 <div class="view">contents99</div>
上記は99まで連続生成されるものとします。
ここである特定位置に新しい要素が追加される場合、何もしないままだと全てのレンダリング(99個)が再度行われます。
イベント発生後
<div class="view">contents1</div> <div class="view">contents2 <div class="new-content">Attention!</div> </div> <div class="view">contents3</div> <div class="view">contents4</div> …以下繰り返し処理 <div class="view">contents99</div>
本来は2段目の箇所だけ反映がされればいい訳です。他の箇所には一切の変更はありません。
しかしこの様なイベントが発生すると、その度ごとに全ての要素のスタイルやレイアウトの計算をしなければならなくなります。
つまりブラウザは毎回無駄なレンダリング処理をさせられている事になりますね。
ユーザー入力に迅速に対応できない
コンテンツ量が大きくなればなるほど再計算時の処理量が増えるので、ページによってはユーザーの入力に迅速にレスポンスできない可能性が高まります。
通信環境やユーザー環境によって、そのレスポンスに常に差が出る事も懸念されます。
開発者から積極的にブラウザに伝える
最近はブラウザ側も高性能になり、レンダリングの範囲を自動的に制限する様になったので、ある程度処理は早くなりました。
「CSS containment」は、この対象範囲を開発者自身がコントロール(封じ込め)できるCSSプロパティです。
開発者本人なら、コンポーネントがイベントごとにどのコンポーネント部分に影響を「与える・与えない」かが分かるはずです。
ならばそのルールを、CSSで積極的にブラウザに教えてあげようというのが主旨になりますね。
ブラウザ実装状況
CSS containmentはW3C勧告になり、WEB標準の機能としてSafari以外の最近のブラウザで実装されています。
ブラウザ実装状況:https://caniuse.com/css-containment
containプロパティを使う事によって、ブラウザは生成コンテンツ全体ではなく限定された領域を対象にレイアウト、スタイル、サイズを再計算できるようになります。
特に大きな容量のコンテンツにおいては、再フローや再描画のパフォーマンスを大幅に改善する事ができますので、ほとんどのブラウザで導入されています。
containプロパティ
CSS containmentは、containという新しいプロパティに以下の4つの値を指定して使用します。
基本の4つの値による封じ込め
・contain: layout
・contain: paint
・contain: size
・contain: style
各値はそれぞれブラウザが処理するべきレンダリングの振る舞いを制限します。それぞれの値についてみてみましょう。
styleについて
レベル2仕様から「style」が追加されています。
レベル1仕様では削除されていたため、推奨事項には表示されずFirefoxには実装されていません。
contain: layout
この値を指定された要素はレイアウトの封じ込めがおこなわれます。
これにより封じ込められた要素はそれ以外に影響しなくなりますし、逆に要素以外からの力が封じ込められた要素に影響する事もありません。
後述するcontain: paintも、layoutと同様のレイアウトの封じ込め効果を持っています。
基本的にレンダリングの処理量はその生成コンテンツの大きさに比例して大きくなります。
つまり要素内のプロパティを変更した場合、コンテンツ内の全ての要素に対しチェックが働く事になります。
封じ込めを有効にしておけば、その範囲内はほぼ「レンダリング差」が起きないので負担が軽くなります。
これによりブラウザが余計な仕事をする必要がなくなり、パフォーマンスが大きく向上する訳です。
contain: paint
この値もlayoutと同様、要素に対して描画の封じ込めを有効にします。
これにより子孫要素は、指定された要素の領域外(エリア外)には表示されなくなります。
もし指定された要素がエリア外もしくは非表示となる場合は、子孫要素自体表示がされません。
この様に描画されるエリアを限定する事はレイアウトと同様にとても有益な封じ込めとなります。
layout、paintにおける3つの副作用
このlayout、paintによる封じ込めを行った場合、主に3つの作用が起こります。
絶対配置の要素になる
封じ込めされた要素は、絶対配置の様に位置を固定されたコンテナブロックのような形になります。
position:absoluteの様な配置になり、親の要素とは独立したものになります。
スタッキングコンテキストになる
これによりz-indexなどが作動し、その子要素も新しく生成されたコンテキストに沿ってスタックされます。
フォーマティングコンテキストになる
例えば描画の封じ込めを指定したブロックレベル要素がある場合、その中は独立したレイアウト環境として扱われます。
本来外側から届くはずのレイアウトやスタイルの命令が、要素内には影響しなくなります。
contain: size
この値は要素に対しサイズ的な封じ込め効果を持ちます。
contain: sizeを指定された要素は子要素サイズに関係なく、常に指定された大きさでレイアウトされます。
ですので大きさが指定されていないと、要素は「0px×0px」で表示されるので注意が必要です。
contain: style
この値は要素に対してスタイル的な封じ込め効果があります。
要素のスタイルを変更した場合、コンテンツのツリーの上部分に影響する場合があります。
contain: styleにより、その影響はツリー上部などの要素外には及ばなくなります。
一つの例としてCSSのカウンタがあります。
例:CSSカウンタ
CSSカウンタはページ内の見出し番号を自動的に振るのに使ったりします。
カウンタは本質的にCSSが管理する変数であり、CSSの規則によって何回使用されたかを追跡するものです。
通常、子要素内でカウンタの値が変わると、ドキュメント中にある同じ名前を持つカウンタの値に影響するのです。
カウント計算が途切れる
ここでcontain: styleを指定すると、子要素内のカウンタが変わっても外にあるカウンタ値が変更されなくなります。
この様な意味でcontain: styleは、スタイル変更を一部の対象範囲に制限するものと言えます。
その他のプロパティ
上記の4つの値は「contain: layout paint」のように複数設定でき、組み合わされた複数の挙動をします。
この複数設定をした事と同じになるプロパティ値が、別名で2つ用意されています。
・contain: strict…「contain: layout style paint size」と同じ
・contain: content…「contain: layout style paint」と同じ
contain: strict
要素の大きさがわかっている時(もしくは大きさを保持したい時)にはstrictがよいでしょう。
strictを指定する場合はsizeによる効果が含まれています。
ですので大きさを指定していない場合、要素が「0px×0px」で表示される可能性があるので注意して下さい。
contain: content
一方contentであれば、大きさを指定する必要がありません。
ですので基本的にはcontain: contentの方を使い、それでは不十分な場合にstrictを使う様にしましょう。
containによる封じ込めの例
ここまで説明をしましたが、封じ込めをする事でどのようにブラウザ側が楽になるのか、実例をもとに見ていきます。
動的ソース例(DOM)
以下の様に連続する記事ページがあったとします。
CSS
img { float: left; margin: 10px; border: 3px dotted #999; } article { margin-bottom: 25px; border: 1px dotted #999; }
生成ソース
記事の見出し
記事の詳細文章がここに入ります。
記事の見出し(サムネイル版)

記事の詳細文章がここに入ります。
←サムネイル画像
画像挿入による再レンダリング
ここで1つ目の記事の下に別の画像が特別に挿入されるとしましょう。
DOMツリーが再レイアウト・再描画されるにあたり、2つ目の記事のレイアウトにも支障をきたしてしまいます。
生成ソース
記事の見出し
記事の詳細文章がここに入ります。

記事の見出し(サムネイル版)

記事の詳細文章がここに入ります。
上記の様にサムネイル画像に設置したフロート要素が原因で、最初の画像が2つ目の記事のエリア内にはみ出ています。
この時ブラウザは「記事タイトルの隣に画像を移動させた状態」を再度レンダリングしなければなりません。
layout、paintによる封じ込め
ここで、articleにcontain: layout、或いはpaintの値を設定します(contentでもOK)。
CSS
img { float: left; border: 3px solid black; } article { margin-bottom: 25px; border: 1px solid black; contain: layout; }
生成ソース
記事の見出し
記事の詳細文章がここに入ります。

記事の見出し

記事の詳細文章がここに入ります。
画像のfloatが枠内に収まっているように見えます。
新しい要素が挿入された際ブラウザはそれが含まれる要素のサブツリーのみを再計算し、その外側には何も影響しなくなったためです。
この時ブラウザは、画像が挿入された要素のみの再描画で済みます(2つ目の記事部分に変化がないため)。
これが封じ込めをする事でブラウザの負担を少なくできる要因です。
sizeを入れた場合
ではもしここで、articleにcontain: sizeの値を設定したらどうなるでしょう。
CSS
img { float: left; border: 3px solid black; } article { margin-bottom: 25px; height: 150px; //縦のみサイズ指定 border: 1px solid black; contain: size; }
生成ソース
記事の見出し
記事の詳細文章がここに入ります。

記事の見出し

記事の詳細文章がここに入ります。
sizeは子であるサムネイル画像のサイズに関係なく、その要素の寸法を指定できることを示します。
要はこの時ブラウザは、中にある要素にどのような変更があろうと、親要素サイズの再描画をしなくて済む事になります。
この例ではartcleに縦サイズの150pxのみを維持した形になっています。
ですので、わかりにくいかも知れませんがサムネイル画像などの子要素は外に出ています。
size paint(strict)を入れた場合
ここでさらに余談ですが、articleにcontain: size paintの複数値を設定してみます。
CSS
img { float: left; border: 3px solid black; } article { margin-bottom: 25px; border: 1px solid black; contain: size paint; }
生成ソース
記事の見出し
記事の詳細文章がここに入ります。

記事の見出し

記事の詳細文章がここに入ります。
paintが入る事により、要素の外にはみ出ている部分は描画されません。
今回はsizeにより中の要素自体も完全に外に出ているので、親も子も全く映らない状態になりました。
まとめ
以上、CSS containment属性についてご紹介しました。
このプロパティはユーザーに対して摩擦のかからない安全な機能です。
ブラウザがこのプロパティをサポートしている・していないにかかわらず、全てのWEBサイトに追加でき、どのブラウザユーザーにも安全に利用できます。
マイナスにはならない機能
ブラウザが封じ込めをサポートしていなければ通常通りのパフォーマンスであり、サポートしていればよりパフォーマンスが向上するというだけです。
動的処理をしているコンテンツ部分があれば、積極的にCSS containmentを活用していきましょう。
それにより普段以上のパフォーマンス向上につながる可能性があります。