【実務・中級編】 nth-child(an+b of selector) の構文 – CSS実践ガイド

おい、ちょっといいか。最近のCSSの進化、マジメに追いつけてるか?

現場でコードレビューをしてると、いまだに「あー、これ昔ながらの無理やりなDOM構造にしてJavaScriptで無理やりクラス付けちゃってるな……」っていうコードに遭遇して、こっちの胃が痛くなることがあるんだよな。

CSSってのはな、ただ画面を装飾するためのツールじゃねぇ。ブラウザという名の優秀なレンダリングエンジンに「何をどう見せたいか」を正確に伝えるためのロジックそのものなんだ。

今回は、そんなCSSの中でも、近年のモダンフロントエンド開発において「知っているか知らないかで実装工数が3倍変わる」と言っても過言ではない、最強の奥義 `:nth-child(an+b of selector)` について徹底的に叩き込んでやる。
公式ドキュメントの乾燥した文字列を読むだけじゃ絶対にわからない、ブラウザの裏側の挙動や、現場で即座に使える実践的な知見をシェアしよう。心して読め。

—

1. そもそも `:nth-child(an+b of selector)` ってなんだ?

お前ら、従来の `:nth-child()` は使ったことあるよな?「親要素の中にある、上から数えて◯番目の要素」を指定するやつだ。

例えば、こんなリストがあったとする。

  • りんご
  • みかん(特別)
  • バナナ
  • ぶどう(特別)

ここで「`.special` クラスがついている要素だけを対象にして、交互に背景色を変えたい」と思ったとする。
昔の感覚のまま `:nth-child(odd) .special` なんて書いたヤツ、今夜は残業決定な。それじゃ「上から奇数番目の要素が、たまたま `.special` を持っていた場合」しかヒットしねぇ。`.special` だけを綺麗にカウントして交互にスタイリングするなんて、昔はJavaScriptの力を借りるか、HTML側に無駄なmodifierクラスを生やすしかなかった。

そこで登場したのが、今回解説する `of selector` 構文 だ。

/ 「.special」の要素だけに絞り込んで、その中で奇数番目のものを選ぶ /
li:nth-child(odd of .special) {
background-color: #ffeaa7;
}

この `of` の後ろにセレクタを挟むことで、「一度特定の条件で要素をフィルタリングしてから、その中で `an+b` のカウントを行う」 という神がかった挙動が可能になった。これがどれほど革命的か、中級へのステップアップを目指すお前らなら、震えるはずだ。

—

2. ブラウザの裏側で何が起きているのか?(処理の仕組み)

シニアとして、ただ「便利だよ」で済ますわけにはいかない。ブラウザが裏側でどう動いているか、そのパフォーマンスへの影響まで踏み込んでこそ一人前だ。

ブラウザのレンダリングエンジン(BlinkやGeckoなど)がCSSセレクタを評価するとき、基本的には右から左へ(Key Selectorから祖先へ)パースしていく。
従来の `:nth-child(3)` は、親要素の子リストを先頭から順にスキャンし、「3番目かつ自分のタグ・クラスが一致するか」を評価していたから、比較的シンプルだった。

では、`of selector` が加わるとどうなるか?
ブラウザは以下の2ステップの処理を裏で行っている。

1. フィルタリング(Filtering): 親要素の子孫の中から、`of` の後ろで指定されたセレクタ(例: `.special`)に完全に一致する要素のリストを内部的に抽出・構築する。
2. インデックス評価(Indexing): その抽出された仮想的なリストに対して、`an+b`(偶数・奇数やn番目)の計算を適用し、合致した要素にスタイルを適用する。

「それって、ブラウザに重い負荷がかかるんじゃないですか?」って?
鋭い質問だな。だが安心していい。近年のブラウザの最適化技術はすさまじく、このフィルタリング処理はC++レベルのネイティブコードで爆速で処理される。JavaScriptでDOMを走査してクラスを付け替えるコストに比べれば、CSSエンジンに任せた方が圧倒的にメモリ効率もパフォーマンスも上だ。安心してガンガン使ってくれ。

—

3. 現場で即コピペして使える!実践ユースケース3選

理屈はこれくらいにして、実際のプロジェクトでどう使うのか、ヨダレ物の実用サンプルを見せてやろう。

ケースA: 混在するリストから「有効なカード」だけをゼブラストライプにする

デザインカンプでありがちなのが、「広告バナー」や「区切り線」といった本来のリストデータとは違う異物がDOMに混ざっているケースだ。

記事1
記事2
広告

記事3
記事4

これを `.card` だけを対象にして、背景色を交互(ゼブラ)に変えたいとする。

/ 広告バナーを無視して、.cardだけで奇数・偶数を判定する /
.feed-container > .card:nth-child(odd of .card) {
background-color: #ffffff;
}

.feed-container > .card:nth-child(even of .card) {
background-color: #f1f2f6;
}

どうだ? `.ad-banner` が間に挟まってようが関係なく、`.card` だけがきれいに交互の色になる。HTML構造をいじる必要すらない。最高だろ?

ケースB: 動的に増減するタグ要素の「3番目以降」を隠す

例えば、タグクラウドなどで「最初から表示するのは `.tag` クラスを持つ要素の最初の3つだけにして、残りは隠したい」という要件。

/ .tag クラスを持つ要素の中で、4番目以降のものを非表示にする /
.tag-container .tag:nth-child(n + 4 of .tag) {
display: none;
}

「あれ? `:nth-of-type` じゃダメなの?」って思った奴、そこが落とし穴だ。
`:nth-of-type` は「タグ名」でしか絞り込めない。HTMLがすべて `

` タグや `` タグで構成されているようなカオスなCMS出力のマークアップにおいて、クラス単位で何番目かを指定したい場合、この `:nth-child(n+4 of .tag)` が唯一無二の救世主になる。

ケースC: グリッドレイアウトでの特定カラムの調整

CSS GridやFlexboxで並べたカード群の中で、「特定のステータス(例: `.is-featured`)を持つカードが、フィルタリングされた中で何番目にあるか」によってマージンやボーダーを変えたいときにも、この構文は無類の強さを発揮する。

/ 強調表示(.is-featured)されたアイテムが、そのグループ内で2番目以降の場合の処理 /
.grid-item:nth-child(n + 2 of .is-featured) {
border-top: 2px dashed #ff4757;
margin-top: 1rem;
}

—

4. 注意しておけ!実務でハマる「落とし穴」とブラウザ対応

さて、ここまで持ち上げておいて何だが、シニアとして現場の現実も伝えておかねばならない。

1. ブラウザサポートの現状

気になるブラウザの対応状況だが、主要モダンブラウザ(Chrome, Safari, Firefox, Edge)の現在のバージョンでは完全に対応済みだ。Can I useで見ても、グローバルで95%以上のシェアを誇っている。
「社内のレガシー案件でIE11をサポートしなきゃいけないんだ……」なんて地獄のような現場でなければ、今日からプロダクション環境に投入して全く問題ない。

2. 詳細度(Specificity)の罠

CSSの詳細度において、`:nth-child()` などの擬似クラス自体の詳細度は通常のクラスと同等(1, 0, 0)としてカウントされる。さらに、`of selector` の部分に渡したセレクタ(例えば `.special`)の詳細度もそのまま加算される。
「なんかスタイルが上書きできないな?」と悩んだときは、詳細度の計算ミスをしている可能性が高いので、ブラウザのDevToolsでしっかりcomputedを確認する癖をつけろよ。

—

まとめ:CSSをもっと「賢く」使おう

`:nth-child(an+b of selector)` は、ただの「便利な新機能」じゃない。
HTMLの構造に依存しすぎた脆弱なCSSや、JavaScriptによる無駄なDOM操作から私たちを解放してくれる、アーキテクチャの質をワンランク引き上げるための強力な武器だ。

「HTMLのマークアップがこうだから、CSSはこう書くしかない」と諦める前に、現代のCSSがここまで賢くなっていることを思い出してほしい。
お前らの書くコードが、もっとエレガントで、保守性の高いものになることを期待している。

それじゃ、次のコードレビューでクールな実装を見せてくれよ。頼んだぞ!

コメント

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