「最初の要素だけスタイルを変えたい」「最後の3つだけマージンをなくしたい」——こういった位置ベースの指定はCSSでよく書く。:nth-of-type() の基本から、:nth-child の of 構文、:is() / :not() / :has() を組み合わせた「クラスベースの位置選択」まで整理する。
1. :nth-child と :nth-of-type の違い
まず基本の違いを押さえる。どちらも「何番目か」を指定するが、数え方が異なる。
:nth-child(n)— 親要素内の主要な子要素を順番に数える(タグ名を問わない):nth-of-type(n)— 同じタグ名の要素だけを順番に数える
/* dl :nth-child(3) → 子要素の3番目 → [2] list-term(dt) */
dl :nth-child(3) {}
/* dt:nth-of-type(2) → dtの2番目 → [2] list-term */
dt:nth-of-type(2) {}
/* dd:nth-of-type(2) → ddの2番目 → [2] list-data */
dd:nth-of-type(2) {}ポイント:
:nth-childはタグをまたいで数え、:nth-of-typeはタグ名ごとに数える。
2. 順番指定::nth-of-type と :nth-last-of-type
:nth-of-type(An+B) の n は 0, 1, 2, 3... と増える整数。A が周期、B が開始位置。:nth-child も同じ式が使えるが、数え方が上記の通り異なる。
前から数える
[1] list-item
[2] list-item
[3] list-item
[4] list-item
[5] list-item
[6] list-item
/* 最初の要素 → [1] */
:nth-of-type(1) {}
/* 最初以外(2番目以降) → [2][3][4][5][6] */
:nth-of-type(n+2) {}
/* 最初~3番目 → [1][2][3] */
:nth-of-type(-n+3) {}
/* 4番目以降 → [4][5][6] */
:nth-of-type(n+4) {}
/* 4番目~6番目 → [4][5][6] */
:nth-of-type(n+4):nth-of-type(-n+6) {}後ろから数える
/* 最後の要素 → [6] */
:nth-last-of-type(1) {}
/* 最後以外 → [1][2][3][4][5] */
:nth-last-of-type(n+2) {}
/* 最後~後ろから3番目 → [4][5][6] */
:nth-last-of-type(-n+3) {}
/* 後ろから4番目以前 → [1][2][3] */
:nth-last-of-type(n+4) {}
/* 後ろから4番目~後ろから6番目 → [1][2][3] */
:nth-last-of-type(n+4):nth-last-of-type(-n+6) {}前後を組み合わせる
/* 最初と最後以外 → [2][3][4][5] */
:nth-of-type(n+2):nth-last-of-type(n+2) {}
/* 4番目以降かつ後ろから4番目以前 */
:nth-of-type(n+4):nth-last-of-type(n+4) {}単独要素の除外
/* 最初の要素(1つしかない場合は除外) */
:nth-of-type(1):not(:only-of-type) {}
/* 最後の要素(1つしかない場合は除外) */
:nth-last-of-type(1):not(:only-of-type) {}奇数・偶数・グリッド位置
/* 奇数番目(odd キーワードでも同じ) */
:nth-of-type(2n+1) {}
:nth-of-type(odd) {}
/* 偶数番目(even キーワードでも同じ) */
:nth-of-type(2n) {}
:nth-of-type(even) {}
/* 3列グリッドの位置別指定 */
:nth-of-type(3n+1) {} /* 1, 4, 7...列目 */
:nth-of-type(3n+2) {} /* 2, 5, 8...列目 */
:nth-of-type(3n) {} /* 3, 6, 9...列目 */
3n+1/3n+2/3nは「3の倍数の要素」ではなく、3つごとのグループの各位置を指す。
実用例:flex レイアウトの上マージン
列数に応じた折り返し後の上マージン指定。:is() でデータ属性をまとめることで繰り返しを減らせる。
.shelf-wrapper:is([data-cols="2"], [data-cols_pc="2"]) > :is(li, dd):nth-of-type(n+3),
.shelf-wrapper:is([data-cols="3"], [data-cols_pc="3"]) > :is(li, dd):nth-of-type(n+4),
.shelf-wrapper:is([data-cols="4"], [data-cols_pc="4"]) > :is(li, dd):nth-of-type(n+5) {
margin-top: 2pc;
}3. :nth-child の of 構文
Selectors Level 4 で策定された構文。:nth-child(An+B of セレクタ) の形で、特定のクラスや状態を持つ要素だけを対象に「何番目か」を数えられる。
:nth-child(An+B of セレクタ) {}:nth-of-type との違い
dl/dt/dd が混在し、一部の要素だけに .christina クラスがついているケースで比較する。
[1] list-term
[1] list-data
[2] list-term
[2] list-data
[3] list-term
[3] list-data
/* .christina:nth-of-type(1) → タグ名ごとに1番目を探す
→ dt の 1番目かつ .christina → [2] list-term
→ dd の 1番目かつ .christina → [2] list-data
→ 2つヒット */
.christina:nth-of-type(1) {}
/* :nth-child(1 of .christina) → .christina の中で兄弟全体から1番目
→ [2] list-term のみヒット */
:nth-child(1 of .christina) {}
of構文は.christinaを持つ要素を一列に並べ直してから「何番目か」を数えるため、直感通りに動く。
of 構文でのクラス内位置指定
[1] list-item
[2] list-item
[3] list-item
[4] list-item
[5] list-item
[6] list-item
[7] list-item
/* .christina を持つ中で最初の要素 → [2] */
:nth-child(1 of .christina) {}
/* .christina を持つ中で最初以外 → [4][5][6] */
:nth-child(n+2 of .christina) {}
/* .christina を持つ中で最後の要素 → [6] */
:nth-last-child(1 of .christina) {}
/* .christina を持つ中で最後以外 → [2][4][5] */
:nth-last-child(n+2 of .christina) {}
/* .christina を持つ中で最初と最後以外 → [4][5] */
:nth-child(n+2 of .christina):nth-last-child(n+2 of .christina) {}
/* .christina を持つ中で2番目~3番目 → [4][5] */
:nth-child(n+2 of .christina):nth-child(-n+3 of .christina) {}of 構文が真価を発揮するケース
:nth-of-type では対応できない「特定の状態を持つ要素をスキップして数える」用途。
/* [type="hidden"] でない要素の奇数・偶数(ゼブラ縞) */
:nth-child(odd of :not([type="hidden"])) {}
:nth-child(even of :not([type="hidden"])) {}
/* チェック済みを除いたリストの最初 */
:nth-child(1 of li:not(.is-checked)) {}
/* 画像ありのカードだけ最初と最後以外 */
:nth-child(n+2 of .card:has(img)):nth-last-child(n+2 of .card:has(img)) {}
odd/evenは2n+1/2nのエイリアス。of構文でも使える。
4. クラスベースの位置選択::is() / :not() / :has()
「前後の要素との関係で位置を指定する」用途は of 構文ではなく、:has() / :is() / :not() の組み合わせで書く。
[1] list-item
[2] list-item
[3] list-item
[4] list-item
[5] list-item
[6] list-item
隣接・間接関係の指定
/* .christina を持たない .target */
.target:not(.christina) {}
/* .christina の子孫ではない .target */
.target:not(.christina *) {}
/* .christina を持たず、かつ子孫でもない .target */
.target:not(.christina, .christina *) {}
/* .christina の直後の要素 → [3] */
.target:is(.christina + *) {}
/* .christina の後ろにあるすべての要素 → [3][4][5][6] */
.target:is(.christina ~ *) {}
/* .christina の直前の要素 → [1][3] */
.target:has(+ .christina) {}
/* .christina より前にあるすべての要素 → [1][3] */
.target:has(~ .christina) {}.christina クラスを持つ要素の中での位置
/* .christina を持つ中で最初の要素 → [2] */
.christina:not(.christina ~ *) {}
/* .christina を持つ中で最初以外 → [4][5] */
.christina:is(.christina ~ *) {}
/* .christina を持つ中で最後の要素 → [5] */
.christina:not(:has(~ .christina)) {}
/* .christina を持つ中で最後以外 → [2][4] */
.christina:has(~ .christina) {}
:has(~ .christina)は「後ろのどこかに.christinaがある」→ 最後以外。
:not(:has(~ .christina))は「後ろに.christinaがない」→ 最後の要素。直感と逆になりやすいので注意。
5. of 構文 vs :has()/:is()/:not() の使い分け
| 用途 | 向いている構文 |
|---|---|
| クラスや状態でフィルタして「何番目か」を指定 | of 構文 |
| 非表示・チェック済みなどをスキップして数える | of 構文 |
| 前後の要素との相対的な位置関係で指定 | :has() / :is() / :not() |
| 詳細度をゼロに抑えたい | :where() |
6. 「最初以外」の複数の書き方と詳細度
同じ意味のセレクタでも書き方によって詳細度や変更コストが変わる。
/* 最初以外 */
.target:not(:first-of-type) {} /* 詳細度: 0,2,0 */
.target:nth-of-type(n+2) {} /* 詳細度: 0,2,0 */
/* 最初と最後以外 */
.target + .target {} /* 詳細度: 0,2,0(フクロウセレクタ) */
:nth-of-type(n+2):nth-last-of-type(n+2) {} /* 詳細度: 0,2,0 */「最初と最後以外」を「最初から3番目の後と最後から3番目より前以外」に変更したくなった場合、フクロウセレクタ .target + .target は書き換えが大きい。最初から :nth-of-type(n+2):nth-last-of-type(n+2) の形で書いておくと変更が局所的に済む。
:where() を使うと詳細度をゼロにできるため、上書きしやすいスタイルを書きたい場面で有効。
/* 詳細度をゼロにする */
.christina:where(:has(~ .christina)) {} /* 最後以外、詳細度: 0,1,0 */7. 詳細度リファレンス
| セレクタ | 詳細度 |
|---|---|
:nth-of-type(n+2) | 0,1,0 |
:nth-of-type(n+4):nth-of-type(-n+6) | 0,2,0 |
:nth-last-of-type(n+2) | 0,1,0 |
:nth-last-of-type(n+4):nth-last-of-type(-n+6) | 0,2,0 |
:nth-of-type(n+2):nth-last-of-type(n+2) | 0,2,0 |
:nth-of-type(odd) | 0,1,0 |
:nth-child(1 of .christina) | 0,1,0 |
:nth-child(n+2 of .christina):nth-last-child(n+2 of .christina) | 0,2,0 |
.target:not(:first-of-type) | 0,2,0 |
.target:nth-of-type(n+2) | 0,2,0 |
.target + .target | 0,2,0 |
.christina:not(.christina ~ *) | 0,2,0 |
.christina:is(.christina ~ *) | 0,2,0 |
.christina:not(:has(~ .christina)) | 0,2,0 |
.christina:has(~ .christina) | 0,2,0 |
:is()/:not()の詳細度は引数リストの中で最も高い詳細度を引き継ぐ。
of構文の詳細度はof以降のセレクタの詳細度を引き継ぐ(:nth-child(1 of .christina)は0,1,0)。
:where()を使うと詳細度をゼロにできる。

