( 2022/6/2 )トップページのレイアウトを変更しました!

【解説】display: contents|使い方や最新ブラウザ対応状況について

 

CSSのdisplayプロパティに「contents」という値があることを知っていますか?

最近では様々なCSSレイアウト手法が登場し、効率化が図れるようになってきましたが、display: contentsを活用することで、FlexBoxやCSS Gridがより効率的に書けるようになるかもしれません!

当記事では「display: contents」とはなにか?「display: contents」の使い方について解説します。

レイアウト組みで悩んでいるそこのあなたにも、役に立つ情報ですよ!

display: contentsってなに?

displayプロパティは要素の形式を定めるCSSですが、その中の値の一つとして「contents」があります。

contentsを設定した要素は、DOMツリーアクセシビリティツリーから削除されます。

省略されるのはcontentsを指定した要素のみで、子孫要素へは影響を与えません。

「つまりどういうこと…?」となっている方もいますよね。次から少し細かく解説します。

ボックスモデルについておさらい

まずは「はじめてのコーディング③ 〜CSS基礎知識編〜」でも解説しているボックスモデルをおさらいしましょう。

ボックスモデルとはCSSの概念の一つで、「定義された要素はすべて長方形のボックスにおさめられている」という考え方です。

HTMLがブラウザ上に描かれる際、ブラウザのレンダリングエンジンは、HTML内に記述されている要素をボックスモデルに基づいた長方形(ボックス)で認識して表示します。

ボックスは以下の図のように、コンテンツ領域・padding・border・marginの4つで定義されています。

ボックスモデルのイメージ

display: contents の値を設定すると、指定した要素のコンテンツ領域以外は描画されなくなります。

<p>テキスト</p>

コンテンツ領域とはタグで囲んだ中身のことで、上であればpタグの中にある「テキスト」という文字のことです。

つまり、その他のpadding・border・marginがブラウザに描画されなくなる、ということです。

display:none との違い

ブラウザに対する非表示指定の値としては「none」もありますが、違いは、コンテンツ領域を表示するかしないかです。

<div style="display: contents;">
  <p>contentsの例</p>
</div>

<div style="display: none;">
  <p>noneの例</p>
</div>

上のHTMLをブラウザで表示させた結果が次の画像です。

ブラウザの表示

display: contents はコンテンツ領域(pタグと「contentsの例」というテキスト)を描画しているのに対し、display: none はコンテンツ領域含めすべて描画されていません。

検証ツールで確認してみても、display: contents を指定したdiv要素は選択できませんが、子要素にあたるpタグは選択できることがわかります。

これはブラウザに描画されなくなる要素が、display: contents を指定した要素のみに限定されていることを意味します。

display: contentsの具体的な使い方とメリット

display: contentsが具体的にどのような場面で使用されるのか整理しておきます。

  1. HTMLを構造化できる
  2. 不要なラッパー要素となるHTMLを省略できる

display: contentsの便利な実装例

それでは、実際にdisplay: contentsを使用して実装をしてみましょう。

FlexBoxとの組み合わせ

display: flexを使用して、以下のようなボックスを作ります。

Flexboxとdisplay:contentsの組み合わせ①
PC(幅1200)
Flexboxとdisplay:contentsの組み合わせ②
SP(幅360)

こちらのデザインを表現するため、以下のHTMLとCSSを組みます。

<div class="container">
    <img class="thumbnail" src="thumbnail.png" alt="">
    <div class="contents">
        <h2 class="title">ブログのタイトルが入ります。</h2>
        <p class="text">ブログの説明が入ります。</p>
        <button type="button" class="button">Enter</button>
    </div>
</div>
.container {
	border: solid 1px #000;
	display: flex;
	margin: 20px auto;
	max-width: 500px;
	padding: 10px;
}

.thumbnail {
	border: solid 1px #000;
	height: 100px;
	margin-right: 10px;
	width: 100px;
}

.contents {
	display: flex;
	flex-direction: column;
	width: 100%;
}

.button {
	align-self: flex-end;
	background-color: #111934;
	border: 0;
	color: #fff;
	display: block;
	font-size: 16px;
	font-weight: 700;
	padding: 10px;
}

@media screen and (max-width:600px) {
	.container {
		align-items: center;
		flex-direction: column;
		margin: 20px auto;
		width: 85%;
	}

	.thumbnail {
		height: 100px;
		margin: 0 0 10px 0;
		width: 100px;
	}

	.contents {
		align-items: center;
	}

	.title {
		font-size: 20px;
	}

	.button {
		align-self: center;
		margin-top: 10px;
		width: 200px;
	}
}

サムネイル部分のimgタグと、それ以外を囲むdivタグ(contentsクラス)を、containerクラスの子要素としてFlexBoxで組んでいます。

このdivタグ(contentsクラス)にdisplay: contentsを指定してみます。

@media screen and (max-width:600px) {
	.contents {
		/* align-items: center; */
		display: contents;
	}
}

動画0:18あたりで、contentsクラスのdivタグにdisplay: contentsを指定していますが、contentsクラスのdivタグが検証ツール上(DOM)で触れなくなっています。

フレックスアイテム
display: contents指定前imgタグ・divタグ(contentsクラス)
display: contents指定後imgタグ・h2タグ・pタグ・buttonタグ
containerクラスの子要素

また、divタグ(contentsクラス)にdisplay: contentsを指定したことで、containerクラスの子要素(フレックスアイテム)は上記のように変化しています。

<div class="container">
  <img class="thumbnail" src="thumbnail.png" alt="">
  <h2 class="title">ブログのタイトルが入ります。</h2>
  <p class="text">ブログの説明が入ります。</p>
  <button type="button" class="button">Enter</button>
</div>

実際のHTMLコードが変わっているわけではありませんが、DOM上だと上記のような振る舞いになります。

画像以外の要素を囲んでいたdivタグ(contentsクラス)をdisplay: contentsで認識させなくしたことで、4つのタグが兄弟要素のように扱われます。

@media screen and (max-width:600px) {
	.title {
		font-size: 20px;
		order: -1;
	}
}

同列のフレックスアイテムとなったtitleクラスのh2タグに、orderプロパティを指定してみます。

orderプロパティを指定することで、フレックスコンテナ内での並びを変えられます。

orderを使った順番変更イメージ
orderを使ったコード変更イメージ

orderプロパティはフレックスコンテナ内(containerクラス)でのみ適用します。

※イメージ画像のinner-◯は、本来のHTMLコードにおいて、contentsクラス(divタグ)の子要素にあたるタグです。

orderを使った順番変更イメージ
ブラウザ表示結果(タイトル位置変更)

order: -1を指定したことで、オレンジ枠に入っていたtitleクラス(h2タグ)の順番を、imgタグより上に移動させることができました。

本来のHTML階層では、このような指定はできません。

ブラウザが認識する要素の階層が変わることで、フレックスアイテムも変化し、元のHTML階層では干渉できなかった要素へ影響を与えることが可能となります。

異なる階層をまたがった配置変更も、display: contentsを使用することで、HTMLを書き換えずに対応できる例でした。

Gridレイアウトとの組み合わせ

display: gridと組み合わせてソースを簡略化することもできます。

display:gridとdisplay:contentsの組み合わせ

例えば上のようなテーブルをdisplay: gridを使ってコーディングしたとします。

<div class="table">
    <div class="required">必須</div>
    <div class="contents">必須項目の記載をここに行う</div>
    <div class="required">必須</div>
    <div class="contents">必須項目の記載をここに行う</div>
    <div class="optional">任意</div>
    <div class="contents">任意項目の記載をここに行う</div>
    <div class="optional">任意</div>
    <div class="contents">任意項目の記載をここに行う</div>
    <div class="required">必須</div>
    <div class="contents">必須項目の記載をここに行う</div>
</div>
.table {
    width: 500px;
    margin: 20px auto 0;
    display: grid;
    grid-template-columns: repeat(2, auto);
    border-top: solid 1px #000;
}

.required {
    text-align: center;
    color: #ff0000;
    border-bottom: solid 1px #000;
}

.optional {
    text-align: center;
    color: #0000ff;
    border-bottom: solid 1px #000;
}

.contents {
    text-align: center;
    border-bottom: solid 1px #000;
    border-left: solid 1px #000;
}

簡易的な表なので上記のCSSだけで実現はできますが、表の行や列が増えてくるとHTMLからテーブルの構造が分かりづらくなってきます。

そこで各行にrowクラスを付与した親要素を追加してテーブルの行をまとめ、それぞれにCSSでdisplay: contentsを指定します。

<div class="table">

	<div class="row">
		<div class="required">必須</div>
		<div class="contents">必須項目の記載をここに行う</div>
	</div>

	<div class="row">
		<div class="required">必須</div>
		<div class="contents">必須項目の記載をここに行う</div>
	</div>

	<div class="row">
		<div class="optional">任意</div>
		<div class="contents">任意項目の記載をここに行う</div>
	</div>

	<div class="row">
		<div class="optional">任意</div>
		<div class="contents">任意項目の記載をここに行う</div>
	</div>

	<div class="row">
		<div class="required">必須</div>
		<div class="contents">必須項目の記載をここに行う</div>
	</div>

</div>
/* 追加 */
.row {
    display: contents;
}

このようにまとめることで、テーブルに並ぶ要素数がHTMLからも分かりやすくなります。

さらにdisplay: contentsを指定することで、グリッドコンテナ(tableクラス)の影響を受けずレイアウトができます。

動画を見ると、display: gridを指定したはじめの状態では、各行が縦並びになっていますが、grid-template-columns: repeat(2, auto)を指定することで、2列のグリッドラインができ、rowクラスの要素がグリッド上に並びます。

その後、グリッドアイテムとなっていたrowクラスにdisplay: contentsを指定することで、子階層の各要素がグリッドアイテムに変化し、デザイン通りのレイアウトとなります。

例のデザインくらいであればコーディング方法はもっとありますが、CSSの装飾なども考慮したときに、display: contentsを用いた要素の非表示制御が役に立つシーンもあるでしょう。

display: contentsのブラウザ対応状況

display:contentsのブラウザ対応状況
引用:“contents” | Can I use…

最新版であれば、主要ブラウザはほぼサポートしています。

IE

IEだとdisplay: contentsは使用できません。

ただ、IEについては2022年6月16日にMicrosoft社のサポート終了予定となっているため、今後の支障は考慮しなくてよいでしょう。

Safari

Safariでは部分的に使用することができるとあります。

以前までdisplay: contents を指定した要素が読み上げソフトで読み上げられない事象がありましたが、今年のリリースで、アクセシビリティの初期サポートが開始したと情報がありました。

まとめ

本記事では、以下のポイントを解説しました。

  • display: contentsの表示
  • display: flexと組み合わせて使う方法
  • display: gridと組み合わせて使う方法

表示の制御ができるdisplay: contentsは、HTMLの階層で挙動が左右される、flexやgridとの組み合わせで特にわかりやすく効果を発揮しそうです。

まだまだ新しいプロパティですので、アイデア次第でより便利な使い方ができる可能性も秘めています。

当記事を参考に、ぜひ導入してみてください!