CSSの魔術「:nth-child()」を完全攻略する:an+b記法とブラウザの裏側
こんにちは。フロントエンドの現場で日々CSSの「メンテナンス性」と「可読性」のバランスに頭を悩ませている諸君、お疲れ様。
今日は、CSSの構造的疑似クラスの中でも、特に強力かつ誤解されがちな `:nth-child()` について深掘りしていく。ただ「なんとなく使っている」状態から、ブラウザのレンダリングエンジンがどうDOMを走査しているかを理解し、意図した通りにスタイルを制御できる「真の使い手」になろう。
—
1. :nth-child()の正体:an+b記法のロジック
`:nth-child()` の引数に渡すのは単なる数値じゃない。これは「インデックスの数式」だ。
`an+b` という形式を分解するとこうなる。
- `n`: 0から始まる整数(0, 1, 2, 3…)
- `a`: ステップ数(周期)
- `b`: オフセット(開始位置)
ブラウザは内部で `n` に0から順に代入し、計算結果が「要素のインデックス(1始まり)」と一致した時にスタイルを適用する。
例えば `:nth-child(3n+1)` なら:
- n=0: 1番目の要素
- n=1: 4番目の要素
- n=2: 7番目の要素…
と、3つ飛ばしでスタイルを当てていくわけだ。
—
2. 現場で使う「定石」と「落とし穴」
実務でよく使うパターンを整理した。まずはこれらを手に馴染ませてほしい。
実用的なコードサンプル
/ 1. 基本:偶数行と奇数行の塗り分け(ストライプテーブルなど) /
/ odd (2n+1) / even (2n) は頻出 /
.list-item:nth-child(even) {
background-color: #f9f9f9; / 偶数番目に背景色 /
}
/ 2. 最初と最後を除外する(境界線の制御) /
/ 最初の要素には上線を引かない、といった処理に必須 /
.list-item:not(:first-child):not(:last-child) {
border-top: 1px solid #ddd;
}
/ 3. 「最初の3つ」だけスタイルを当てる /
/ nが0, 1, 2のとき、つまり1, 2, 3番目が対象 /
.list-item:nth-child(-n+3) {
font-weight: bold;
}
/ 4. 3つ目以降すべて /
.list-item:nth-child(n+3) {
margin-top: 20px;
}
—
3. ブラウザが裏側でやっていること
ここで少しだけ技術的な話をしよう。`:nth-child()` は、「親要素の中にある子要素」を、タグの種類に関係なく「順番に」数える。
もし君が「特定のクラスが付いた要素だけを数えたい」と思って `:nth-child(.class)` と書いても、それは期待通りに動かない。なぜなら、ブラウザは「そのクラスを持つ要素」を数えるのではなく、「親から見て何番目の子か」を確認してから「その要素がそのクラスを持っているか」を判定するからだ。
もし「特定のクラスを持つ要素だけをカウントしてスタイルしたい」なら、`:nth-of-type()` を検討するか、あるいは最新の `:has()` との組み合わせを考える必要がある。ここを混同していると、後で手痛いバグに遭遇することになるぞ。
—
4. シニアからのアドバイス:保守性を高めるために
最後に、現場のコードレビューでよく見る「修正しにくいコード」の例を挙げておく。
避けるべき書き方:
/ 意味が直感的に伝わりにくい /
.item:nth-child(4) { … }
これだと、要素が動的に増減する際や、途中に別の要素が挿入された瞬間にスタイルが崩れる。
推奨する考え方:
「なぜその要素を狙うのか」を明確にしよう。もし特定の順序に依存するデザインなら、それはCSSの責務を超えている可能性がある。本当に必要なのは `nth-child` か、それともコンポーネント設計(クラスの付与)を見直すべきか。
—
まとめ:道具を使いこなすということ
`:nth-child()` は非常に強力だが、あくまで「構造」に依存したセレクタだ。
「コンテンツの順序が変わってもデザインが崩れないか?」
この問いを常に持ち続けることこそが、中級者から一歩先へ進むためのマインドセットだ。
今日の解説が、君のコーディングの解像度を少しでも高めるきっかけになれば嬉しい。何か疑問があれば、いつでも聞いてくれ。現場からは以上だ。

コメント