「なぜかスタイルが当たらない」を卒業する::first-of-type と仲間たちの深淵
フロントエンドの現場で、「よし、このリストの最初だけにボーダーを引こう」と思って `:first-child` を書いたのに、なぜか意図しない要素にスタイルが当たって絶望した経験はありませんか?
CSSの擬似クラスは一見単純そうに見えて、ブラウザのレンダリングの仕組みを知らないと足元をすくわれる「地雷」になりがちです。特に `:first-of-type` や `:last-of-type`、そして `:only-of-type` は、DOM構造が複雑化する中規模以上のプロジェクトで、保守性を担保するために不可欠なツールです。
今日は、公式ドキュメントには書いていない「ブラウザがどう動いているか」という裏側のロジックと、現場で「事故らない」ための使いこなし術を伝授します。
—
`:first-child` との決定的な違い
まず、基本の整理です。多くのエンジニアが混同するのが「子要素の順番」なのか「型(タグ名)の順番」なのかという点です。
- :first-child: 「親要素から見て、兄弟の中で一番最初の要素」を指す。タグの種類は問わない。
- :first-of-type: 「親要素の中で、そのタグ名と同じ型を持つ要素のうち、最初のもの」を指す。
この違いは、DOMの中に `h2` と `p` が混在しているような状況で顕著になります。
タイトル
前書き
本文1
本文2
ここで `p:first-child` と書くと、何も選択されません。なぜなら、親から見て最初の要素は `h1` だからです。しかし、`p:first-of-type` と書けば、`h1` を無視して、型が `p` である要素の先頭、つまり「前書き」の段落だけをピンポイントで射抜くことができます。
—
ブラウザの裏側で起きていること
ブラウザが `:first-of-type` を評価する際、単に「最初の子かどうか」を見ているわけではありません。
1. タグ型の絞り込み: まず、対象となるセレクタのタグ型(例: `p`)を確認する。
2. カウンターの初期化: 親要素内を上から順に走査し、同じタグ型が現れるたびにカウントを増やす。
3. 判定: カウントが「1」である要素にスタイルを適用する。
この仕組みを知っておくと、「なぜこの擬似クラスが効かないのか?」というデバッグが劇的に速くなります。DOMの中に予期せぬ要素(`div` ラッパーやコメントノードなど)が紛れ込んでいても、`:of-type` 系なら型さえ一致していれば無視して追いかけてくれるため、マークアップが少し揺らいでもレイアウトが崩れにくいという強みがあります。
—
現場で即戦力になる実装パターン
中級者であれば、単に使うだけでなく「再利用性」を意識した書き方を身につけたいところです。
/ 現場でよくある「最初と最後で余白を変える」テクニック /
.content-list {
display: flex;
flex-direction: column;
gap: 16px;
}
/ 最初の要素の上部余白をリセット /
.content-list > p:first-of-type {
margin-top: 0;
}
/ 最後の要素の下部余白をリセットしてコンテナに合わせる /
.content-list > p:last-of-type {
margin-bottom: 0;
}
/ 要素が一つしかない時だけ特別な装飾を当てる /
.card-wrapper > .item:only-of-type {
border: 2px solid #3b82f6; / 青枠で囲む /
padding: 24px;
}
注意点:`:only-of-type` の落とし穴
`:only-of-type` は「その親の中に、その型の要素がただ一つしかない」場合に発火します。つまり、`div` 内に `p` が一つしかなくても、隣に `span` があれば条件を満たします。逆に、同じ `p` が二つあれば、たとえ別の場所に配置されていてもスタイルは適用されません。
—
チーフアーキテクトからのアドバイス
実務において、これらのセレクタを使いこなす際の黄金律は「DOM構造を過信しない」ことです。
将来的に CMS で記事が流し込まれたり、動的にDOMが書き換わったりするコンポーネントでは、特定のクラスに依存しすぎない「型ベースのスタイリング」が非常に強力な防波堤になります。
一方で、あまりに複雑なネストの中で `:last-of-type` を多用すると、後からコードを読んだ人が「どこにスタイルが当たっているのか」を追うのに苦労します。
- 簡単なUI: `:first-of-type` を積極的に使ってコード量を減らす。
- 複雑なレイアウト: クラス名(BEMなど)を優先し、擬似クラスはあくまで「最後の仕上げ(余白調整など)」に留める。
このバランス感覚こそが、シニアエンジニアへの第一歩です。さあ、明日のコーディングでは、機械的なクラス追加を一つ減らして、スマートに擬似クラスで解決してみませんか?

コメント