【実務・中級編】 only-child疑似クラスの条件 – CSS実践ガイド

お疲れ。いいところに目を付けたな。
CSSを書き始めて数年経ち、BEMだろうがTailwindだろうが一通りレイアウトが組めるようになった中級エンジニアが、次に直面する壁——それは「DOMの構造変化に強い、しなやかなコンポーネント設計」だ。

APIから動的に流し込まれるリストや、CMSから出力されるフリーテキストのカードなど、実務の現場では「子要素が1つしかない時」と「複数並んでいる時」でレイアウトを華麗に切り替えたいシーンが山ほどある。
そんな時、JavascriptでDOMの数を数えてクラスを付け外ししたりしていないだろうか?

おいおい、ちょっと待て。それ、CSSの `:only-child` 疑似クラスで一発レッドカード(解決)だ。
今回は、この孤高の疑似クラス `:only-child` の仕様の裏側と、現場で明日から使える実践的なレイアウト技法を叩き込んでやる。しっかりついてこい。

—

1. `:only-child` の基本仕様と「ブラウザの裏側の動き」

まずは基本のおさらいだ。`:only-child` は、その名の通り「親要素にとって、唯一の子要素である場合」にのみマッチする。

ここで中級者が一番やりがちな勘違いを指摘しておこう。
「あぁ、要するに親の中に要素が1つしかない時ね」と思ったそこの君、半分正解で半分不正解だ。

正確なCSSの仕様(セレクタの定義)において、`:only-child` は 「兄弟要素を持たない要素」 を指す。つまり、DOMツリー上で同じ階層(親から見た子たち)の中に、自分以外の要素が「一切存在しない」状態の時だけ発火する。

ブラウザはどうやってこれを判定しているのか?

ブラウザのレンダリングエンジン(BlinkやGeckoなど)は、HTMLをパースしてDOMツリーを構築する際、各ノードに対して親ポインタと兄弟ノードへの参照を持たせている。
`:only-child` が評価される時、ブラウザは以下の条件をコンマ数ミリ秒の裏側でチェックしている。

1. 自分自身の親要素(Parent Node)を特定する。
2. その親要素が持つ「子リスト(ChildNodes)」の総数を確認する。
3. テキストノードやコメントノードを除外した上で、要素(Element)の総数が「ちょうど1つ」であるかどうかを判定する。

そう、ここがポイントだ。HTMLのソースコード上でタグとタグの間に改行やインデントのスペースがある場合、それは「テキストノード」として扱われるが、`:only-child` は要素(Element)の数をカウントするため、改行スペースの有無によって判定が狂うことはない。安心してくれ。

—

2. なぜ `:only-child` が実務で重宝されるのか?

実務でなぜこれが神機能かというと、「コンポーネントの自律性(Autonomous Component)」を高められるからだ。

例えば、カードUIを想像してほしい。
カードの中に「タイトル」と「説明文」が入るのが標準だが、たまに「タイトルだけ」のカードや、「画像だけ」のカードが流れてくることがある。

もしここで `:only-child` を使わなければ、テンプレート側で条件分岐を書き、クラス名(例: `.is-single`)を付与する泥臭いJavaScriptやサーバーサイドのロジックが必要になる。
だが、CSSにレイアウトの判断を任せれば、マークアップは常に一定のままで、CSS側が勝手に「お、こいつ一人しかいないな」と察知してレイアウトを最適化してくれる。これぞプロのスマートな仕事というやつだ。

—

3. 実践!コピペで使える「単一要素時」のレイアウト調整パターン

百聞は一見にしかずだ。現場でそのまま使える実践的なコードを見ていこう。

今回は、記事カード(Card)コンポーネントを例にする。
通常は「アイキャッチ画像」と「テキストエリア」が縦に並ぶが、画像がない(あるいはテキストエリアの段落が1つだけで他になく、実質的に要素が1つしかない)ような例外ケースを `:only-child` で美しく吸収する例だ。

HTML

サムネイル

CSS設計の極意について

実務で役立つフロントエンドの知見を語ります。

お知らせのみのシンプルなカード

CSS

/ カードのベーススタイル /
.card {
display: flex;
flex-direction: column;
background: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 8px;
overflow: hidden;
padding: 1.5rem;
gap: 1rem; / 通常時の要素間の隙間 /
}

/ ==========================================================
ここが肝:親要素(.card)の中の子要素が「唯一」の場合の処理
========================================================== /
.card > :only-child {
/
唯一の子要素が .card__content だった場合、
親のパディングを相殺してカード全体いっぱいに広げたい、
あるいはレイアウトのグリッドを変化させたい時のアプローチ
/
margin: 0;
padding: 0;
width: 100%;
}

/ さらに踏み込んだ応用:
もし「コンテンツブロック自体がカード内で唯一の要素」かつ
「その中のタイトルが1行だけで寂しい」場合の微調整などにも応用可能。
/
.card__content:only-child {
display: flex;
align-items: center;
justify-content: center;
min-height: 120px;
text-align: center;
background: #f8fafc; / 唯一の要素の時は背景色を変えてアクセントにする等の演出も可能 /
border-radius: 4px;
}

このコードの何が美しいか?
JSのハックや複雑な条件分岐を一切排除し、「DOM構造がどうであれ、CSSのセレクタが文脈を解釈して勝手にスタイルを適応する」という宣言的なスタイリングを完璧に体現している点だ。

—

4. シニアが教える落とし穴とベストプラクティス

最後に、現場で `:only-child` を使う際に、若手がやりがちな失敗とそれを回避するためのプロの知見を授けておく。

トラップ1: 「親要素の直下」を意識し忘れる

`:only-child` はあくまで「その親から見て最初で最後の子供」を指す。
孫要素の階層に潜り込んでいる要素に対して、親のコンポーネントから直接指定しようとしても効かない。

/ 効かない例:.card の中に div があって、その中の p を狙いたい場合 /
.card p:only-child { … }
/
もし .card の直下に p が1個しかなければ効くが、
div でラップされていると、div が :only-child になり、p はその中の一人っ子なので、
HTML構造によっては意図せずヒットしてしまう。階層構造の深さには細心の注意を払え。
/

トラップ2: スタイルの影響範囲が広がりすぎる

コンポーネントが再利用される中で、予期せぬHTMLの変更によって `:only-child` が意図せず発火し、「なんか急にレイアウトが崩れたんだけど!」というバグを生む温床になることがある。
これを防ぐため、必ずコンポーネントのスコープを限定したクラスと組み合わせて使う(例: `.c-card > .c-card__item:only-child`)のが、保守性を保つための鉄則だ。

—

まとめ

`:only-child` は、単なる「1番目の子供を選ぶ疑似クラス」ではなく、「動的なDOMの変化をしなやかに受け止めるためのセマンティックなデザインツール」だ。

フロントエンドのアーキテクトとして、レイアウトの変動要因をJavaScriptに逃げるのではなく、CSSの標準仕様のポテンシャルを最大限に引き出してエレガントに解決する——この姿勢を持つだけで、君が書くコードの品質は一段も二段も跳ね上がる。

次の案件では、無駄なJSの条件分岐を削ぎ落とし、この `:only-child` をクールに実装して周囲をうならせてやれ。期待しているぞ。

コメント

タイトルとURLをコピーしました