Post on:2021年9月4日
sponsorsr
例えばカードで、見出しが1行・3行、本文の量が多かったり少なかったりする場合、それぞれの高さを揃えるのは非常に難しく、かなりトリッキーな実装が必要でした。もしくは、JavaScriptを使用しなくてはできなかった実装です。
こういったレイアウトをセマンティックなHTMLで実装できるようになるdisplay: contents;の基礎知識と使い方を紹介します。

下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
【更新】
2021/9/4: 現在の環境に合わせて、内容をアップデートしました。
今までにも何回か解説してきましたが、ドキュメントツリーのすべての要素は長方形のボックスで構成されています。
広義において、この「長方形のボックス」は2つのセクションで構成されています。
1つ目は、パディング・ボーダー・マージンのエリアで構成された実際のボックスです。2つ目はボックスのコンテンツで、コンテンツ エリアと呼ばれます。

CSSのボックス モデル
CSSのdisplayプロパティを使用すると、このボックスとその子供たちがページ上でどのように描画されるか、定義することができます。
例えば、ボックスをinlineにすると、テキストのように兄弟として配置できます。また、tableにすると、テーブルのように振る舞うことができます。absoluteにすると、ボックスをz軸に配置することさえ可能です。
マークアップで定義された要素がボックスを生成するかどうかを定義するdisplayプロパティには、2つの値しかありません。値をnoneにすると、ボックスまたはそのコンテンツが表示されなくなります。値をcontentsにすると、ボックスのコンテンツは通常通り描画されますが、周囲のボックスは完全に省略されます。
display: contents;が使用された時に何が起こるのか理解する最も簡単な方法は、要素の開始タグと終了タグがマークアップから省略されていることを想像することです。
W3Cの仕様によると、
For the purposes of box generation and layout, the element must be treated as if it had been replaced in the element tree by its contents
ボックス生成とレイアウトの目的において、要素は要素ツリー内のコンテンツで置き換えられたかのように扱われなければならない。
実際の例を見てみましょう。まずは、マークアップです。
1 2 3 4 | <divclass="outer"> I’msomecontent <divclass="inner">I’msomeinnercontent</div> </div> |
これに、スタイルを適用します。
1 2 3 4 5 6 7 8 9 10 | .outer{ border:2pxsolidlightcoral; background-color:lightpink; padding:20px; } .inner{ background-color:#ffdb3a; padding:20px; } |
ページ上では通常、下記のように表示されます。

ページ上での表示
.outer要素に、display: contents;を追加すると、下記のように表示されます。

.outer要素に、display: contents;を追加
視覚的に言うと、上記の結果は.outer要素の開始タグと終了タグが省略されたマークアップで期待される結果とまったく同じになります。
HTMLにすると、下記のような感じです。
1 2 | I’msomecontent <divclass="inner">I’msomeinnercontent</div> |
このプロパティは一見、単純明快そうに見えますが、多くの知っておくべきケースと特定のビヘイビアがあります。
display: contents;はページ上に視覚的に描画されるボックスにのみ影響します。ドキュメント内のマークアップには影響しません。
要素がdisplay: contents;に置き換えられた場合、それに適用されている属性はどうなるでしょうか?
この置換はほとんどの場合、視覚的にのみ行われるため、属性を使用して要素を選択したり、ターゲットにしたり、インタラクションさせることは可能です。例えば、要素をidでターゲットにする場合には、aria-labelledbyを使用して要素を参照します。
1 2 | <divid="label"style="display: contents;">Labelhere!</div> <buttonaria-labelledby="label"><button> |
しかし、これが正しく動作しないことが分かったのは、フラグメント識別子を使用して要素にナビゲートできなくなったことです。
1 2 3 4 5 6 | <divid="target"style="display: contents;">TargetContent</div> <script> window.location.hash="target"; // => Nothing happens </script> |
これまで説明したように、display: contents;は適用された要素をターゲットにできます。実際、display: none;が適用された要素をターゲットにすることはできますが、インタラクションができないためイベントはトリガーされません。しかし、display: contents;が適用された要素のコンテンツはまだ目に見えるため、そのコンテンツを通して要素とやりとりすることができます。
例えば、要素のクリックに対してイベントリスナーを設定し、thisで値をログに記録すると、外部要素はドキュメント内にまだ存在するため、その要素を取得します。
1 2 3 4 5 6 7 8 | <divclass="outer">I’msomecontent</div> <script> document.querySelector(".outer").addEventListener("click",function(event){ console.log(this); // => <div></div> }); </script> |
display: contents;が適用された要素の擬似要素は、その子要素の一部とみなされるため、通常通り表示されます。
1 2 3 4 5 6 7 | <style> .outer{display:contents;} .outer::before{content:"Before"} .outer::after{content:"After"} </style> <divclass="outer">I’msomecontent</div> |
このマークアップは、下記のように表示されます。

期待通りに表示
置換された要素といくつかのフォーム要素はdisplay: contents;が適用されると、通常とは異なる動作をします。
<button>要素と<a>要素は、display: contents;で特別な動作はしません。ただし、このルールは確定ではない可能性があるため、どのように影響するかを知っておくことは大切です。
わたし達は今まで、HTMLをセマンティックにし、CSSでスタイルするために使用しなければなりませんでした。そのため、ラッピングを目的のために余分な要素が多すぎたり、直接の兄弟スタイルを可能にする要素が少なすぎたりする場合もありました。兄弟スタイルについては今のところ、直接の兄弟要素で作業する必要があるCSS Gridの導入に関係があります。
例えば、下記のレイアウトを見てください。

2つのカードがあり、それぞれに見出し・パラグラフ・フッタが配置されています。ここで望むのは、それぞれの量にかかわらず、2つのカードのそれぞれを同じ高さにすることです(例えば、見出しは左では1行ですが、右では3行です。見出しの量が異なっても、同じ高さにすることを望みます)。
CSS Gridを使用してこのレイアウトを実装することができますが、各カード内のすべての要素が互いに直接の兄弟にする必要があります。
兄弟関係にするためのHTMLを見てましょう。
1 2 3 4 5 6 7 8 9 | <divclass="grid"> <h2>Thisisaheading</h2> <p>...</p> <p>Footerstuff</p> <h2>Thisisareallyreallyreallysuperduperloooongheading</h2> <p>...</p> <p>Footerstuff</p> </div> |
このHTMLにスタイルを適用します。
1 2 3 4 5 6 7 | .grid{ display:grid; grid-auto-flow:column; grid-template-rows:auto1frauto; grid-template-columns:repeat(2,1fr); grid-column-gap:20px; } |
確かに、この実装でレイアウトを実現できますが、HTMLは構造化されてなく、美しくありません。
ここで、display: contents;の登場です。
各カードのコンテンツを<article>でグループ化し、display: contents;を適用します。ページ上に表示される際には、<article>要素は取り除かれます。
1 2 3 4 5 6 7 8 9 10 11 12 | <divclass="grid"> <articlestyle="display: contents;"> <h2>Thisisaheading</h2> <p>...</p> <p>Footerstuff</p> </article> <articlestyle="display: contents;"> <h2>Thisisareallyreallyreallysuperduperloooongheading</h2> <p>...</p> <p>Footerstuff</p> </article> </div> |
意味をなすセマンティックなマークアップが実現でき、レイアウトに適したスタイルを適用させることで、HTMLとCSSのベストを尽くすことができます。
2021年9月現在、display: contents;はIEを除くすべての主要ブラウザでサポートされています。

サポートされていないブラウザに使用する場合は、適切なフォールバックを使用します。
1 2 3 4 5 6 7 8 | article{ display:grid; grid-template-rows:200px1frauto;/* 例えば、ヘッダの高さを固定する時 */ } @supports(display:contents){ article{display:contents;} } |

sponsors