【実務・中級編】 only-of-type擬似クラスの適用条件 – CSS実践ガイド

やあ。今日も今日とてCSSの沼にハマっているかい?
フロントエンドの現場で長く生き残っていると、「あれ、このセレクタ、頭の中では完璧に動くはずなのに、なんでブラウザはスタイリングしてくれないんだ……?」っていうデスマーチの1歩手前みたいな瞬間に何度も遭遇するよね。

今日は、そんなCSSの挙動のなかでも、中級者から「おっ」と声が出る質問をよく受けるテーマを取り上げよう。
`:only-of-type` 擬似クラスだ。

「親要素の中で、そのタグ型の要素が唯一である場合にマッチする」っていう、教科書通りの定義自体はみんな知っている。だけど、実際のコンポーネント開発や動的なCMSの出力結果にこれをブチ込んだ瞬間、なぜかスタイルが効かなくて頭を抱えるエンジニアが後を絶たない。

今日は、この `:only-of-type` がブラウザの内部でどう処理されているのかという「裏側の仕組み」から、実務で絶対にハマる罠、そして現場で即座に使える美しいコードパターンまで、シニアの視点からみっちり解説してあげる。コーヒーでも飲みながら聞いてくれ。

—

1. `:only-of-type` の標準仕様と、ブラウザの「裏側の脳内処理」

まずは基本のおさらいと、ブラウザがDOMツリーをどう解釈しているかの話からいこう。

`:only-of-type` は、「ある親要素(Parent)の子孫の中に、指定されたタグ型(Type)の要素が『1つしか存在しない』場合」にターゲットにヒットする。ここで非常に重要なポイントが2つある。

1. 「クラス名」ではなく「タグ名(要素の型)」で判定されること
2. 「親要素の直下(子)」ではなく、親要素の中にある「すべての同名タグ」がカウントされること

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

ブラウザがレンダリングツリーを構築するとき、セレクタを右から左へ(正確にはマッチングの最適化をしつつ)評価していく。
`:only-of-type` に遭遇したとき、ブラウザはこういう思考プロセスを辿る:

> 「ふむ、この要素の親を見てみよう。親の中には、この要素と同じタグ(例: `

` なら `

`)が、兄弟要素含めていくつ存在しているかな……? おっ、数えてみたら『1個』だけだ。よし、マッチさせよう」

逆に、もし同じ親の中に同じタグが2つ以上あれば、それがどんなに離れた場所にあろうが、クラス名が違っていこうが、容赦なく不成立(アンマッチ)になる。これがこの擬似クラスの最大のクセであり、美しさでもある。

—

2. 現場でありがちな「大誤解」とトラップ

中級エンジニアのコードレビューをしていて一番多い勘違いがこれだ。

> 「`.card` というクラスを持つ要素の中で、唯一の存在だから `.card:only-of-type` でいけるっしょ!」

残念、それだと期待通りに動かないケースが多々ある。
なぜなら、`:only-of-type` は クラスセレクタではなく、暗黙的に(あるいは明示的に)タグ型をベースにしているから だ。

例えば、こういうHTML構造があったとしよう。

カード1
カード2

このとき、`.card:only-of-type` と書いたとする。どうなると思う?
親 `.container` から見ると、子要素のタグは `

` と `

` だ。つまり、親の中に同じタグ型の要素はそれぞれ1つずつ(`

` が1つ、`

` が1つ)しか存在しない。そのため、両方ともマッチしてしまう。
「クラスで絞り込んでいるつもりが、タグ型で判定されている」というこのギャップに、現場のエンジニアは何度も足元をすくわれるんだ。

—

3. 即コピペで使える!実践的なユースケース

じゃあ、この曲者だが強力な `:only-of-type` を、実際のフロントエンド開発でどう使いこなすべきか。
僕たちがよく遭遇する「リスト要素が1件しかない場合のUI切り替え」を例に、きれいなコードを見てみよう。

ユースケース:リストが「1件だけ」の時と「複数」の時のスタイル分岐

CMSやAPIから動的にリストアイテム(`

  • `)が飛んでくるコンポーネントを想像してほしい。
    「データが1件しかない時は、サムネイルを大きくして特別感を出す」みたいなデザイン、よくあるよね。JavaScriptでわざわざ要素数を数えてクラスを付与しなくても、CSSだけでこれが完結する。

    • 注目の新着アイテムです!

    • アイテム1
    • アイテム2

    これに対するCSSの書き方はこうだ。

    / リストコンテナ /
    .item-list {
    list-style: none;
    padding: 0;
    margin: 0;
    }

    / 基本のリストアイテムのスタイル /
    .item-list__item {
    padding: 1rem;
    background-color: #f8fafc;
    border-bottom: 1px solid #e2e8f0;
    transition: background-color 0.2s ease;
    }

    /
    【ここが真骨頂】
    ul(親)の直下にある li(タグ)が、親の中で唯一である場合のみ発火。
    クラス名ではなく、タグ型ベースで判定される点に注意せよ!
    /
    .item-list__item:only-of-type {
    background: linear-gradient(135deg, #6366f1 0%, #a855f7 100%);
    color: #ffffff;
    border-radius: 8px;
    padding: 2rem;
    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
    }

    / 唯一のアイテムの中にあるテキスト要素の調整 /
    .item-list__item:only-of-type .item-list__text {
    font-weight: 700;
    font-size: 1.25rem;
    }

    このコードの美しいところは、JavaScriptを一切汚さずに、CSSの宣言的記述だけで「1件しかない場合の例外的なレイアウト」を担保できる点にある。保守性も抜群に高い。

    —

    4. シニアから後輩へ送る、実務でのベストプラクティス

    最後に、現場で `:only-of-type` を採用する際の心構えと注意点をいくつか共有しておこう。

    1. 混在するタグに気をつけろ
    親要素の中に、異なるタグ(例えば `

    ` の中に `

    ` が1つと、`` が1つ)がある場合、それぞれが「唯一のタグ型」とみなされるため、両方にマッチする。意図しない要素にスタイルが漏れていないか、開発者ツールのインスペクタで必ずDOM構造を確認する癖をつけよう。

    2. コンポーネント設計(BEMなど)との相性を意識する
    BEMなどの命名規則を使っていると、「クラス名単位で制御したい」というメンタルモデルになりがちだ。しかし、構造系擬似クラス(`–of-type` や `–child` 系)は、あくまで「DOMのツリー構造とタグの型」に依存する。この特性を理解した上で、「構造に依存したスタイリングが本当に適切か」をアーキテクチャの観点からジャッジしてほしい。動的にHTMLのタグが変わるような柔軟すぎるコンポーネントには、無理に使わず素直にクラスで制御した方が安全なこともある。

    3. CSSの仕様書の読み解きを楽しめ
    こういう細かい仕様で迷ったときは、MDNはもちろん、W3CのSelectors仕様書に目を通してみると、ブラウザがどういうアルゴリズムでセレクタをマッチさせているかが書いてあってエンジニアとしての知見がグッと深まるよ。

    CSSは、ただ画面を飾るだけのツールじゃない。DOMという木構造を美しく、かつロジカルに支配するための強力なプログラミング言語に近い。
    `:only-of-type` を味方につけて、君のCSSコードベースをもっとスマートで堅牢なものに進化させてくれ。

    それじゃ、次のコードレビューでまた会おう!

  • コメント

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