テーブルの行を1行おきに塗り分けたり、グリッドの3つに1つだけ色を変えたり——そんな「何番目かで狙い撃ち」するスタイルは、CSS の :nth-child() と :nth-of-type() で実現できます。この記事では、偶数・奇数を表す even / odd から、3n や 2n+1 のような an+b 記法、そして混同しやすい :nth-child と :nth-of-type の違いまでを、実際に動かせるコード付きで解説します。CSS の基本は分かるけれど、番号での指定がいまいち掴めないという初心者〜中級者の方が対象です。
目次
「何番目」を狙うとはどういうことか
:nth-child() は、ある要素が「兄弟の中で何番目か」を条件にしてスタイルを当てる擬似クラスです。たとえばリストの3番目の項目だけ、テーブルの偶数行だけ、というように、HTML 側にクラスを増やさなくても順番だけでターゲットを絞り込めます。要素を1つずつ手作業で印付けする必要がないため、項目数が増えても CSS を書き換えずに済むのが大きな利点です。
カッコの中には数字をそのまま書くこともできます。li:nth-child(1) なら1番目、li:nth-child(3) なら3番目です。番号は 1 から数え、0 番目は存在しません。ただし本領を発揮するのは、これから紹介する even / odd や an+b といった「複数の要素をまとめて指定する書き方」です。
偶数・奇数で塗り分ける(even / odd)
もっともよく使うのが、テーブルの行を1行おきに塗り分けるストライプ模様です。偶数番目を表す even、奇数番目を表す odd をカッコに入れるだけで実現できます。下のコードは実際に編集して試せます。CSS タブで even を odd に変えると、色の付く行が入れ替わるのを確認してみてください。
<table class="stripe">
<thead>
<tr><th>ID</th><th>商品名</th><th>価格</th></tr>
</thead>
<tbody>
<tr><td>1</td><td>りんご</td><td>120円</td></tr>
<tr><td>2</td><td>みかん</td><td>80円</td></tr>
<tr><td>3</td><td>ぶどう</td><td>400円</td></tr>
<tr><td>4</td><td>もも</td><td>300円</td></tr>
<tr><td>5</td><td>なし</td><td>200円</td></tr>
<tr><td>6</td><td>かき</td><td>150円</td></tr>
</tbody>
</table>
.stripe {
border-collapse: collapse;
width: 100%;
font-size: 15px;
}
.stripe th,
.stripe td {
padding: 10px 14px;
text-align: left;
}
/* 見出し行 */
.stripe thead th {
background: #4a6cf7;
color: #fff;
}
/* 偶数番目の行に色を付けてストライプ模様にする */
.stripe tbody tr:nth-child(even) {
background: #eef2ff;
}
/* マウスを乗せた行を強調 */
.stripe tbody tr:hover {
background: #dbe3ff;
}
ここで対象にしているのは tbody の中の tr です。見出し行の thead は別の親に属しているため、本文の行だけがきれいに 1, 2, 3… と数えられます。even は 2n、odd は 2n+1 と同じ意味で、数字での書き方に慣れていれば後者で書いても構いません。
an+b 記法で間隔を指定する
:nth-child() のカッコには an+b という数式を書けます。a は「いくつおきか(間隔)」、b は「どこから始めるか(ずらし)」を表します。ブラウザは n に 0、1、2、3… を順に当てはめて計算し、その結果に当たる番号の要素を選びます。たとえば 3n なら、n=1 で 3、n=2 で 6、n=3 で 9 となり、3 つおきの要素が対象になります。
代表的な書き方と、それがどの番号にマッチするかを整理すると次のようになります。
| 書き方 | マッチする番号 | 意味 |
|---|---|---|
3n | 3, 6, 9, … | 3 の倍数(3 つおき) |
2n+1 | 1, 3, 5, … | 奇数(odd と同じ) |
3n+1 | 1, 4, 7, … | 1 番目から 3 つおき |
-n+3 | 1, 2, 3 | 先頭から 3 つだけ |
n+4 | 4, 5, 6, … | 4 番目以降すべて |
少し分かりにくいのが -n+3 です。a がマイナスのときは、n=0 から数えると n=0 で 3、n=1 で 2、n=2 で 1、n=3 で 0 となり、0 以下は無視されるため結果として 1〜3 番目だけが残ります。「先頭からいくつまで」という上限を付けたいときの定番の書き方です。逆に n+4 は「4 番目以降すべて」という下限の指定になります。
実際に組み合わせて使ってみます。下のグリッドでは、3n で 3 つおきのセルを青く塗り、-n+3 で先頭 3 つに枠線を付けています。CSS タブで 3n を 3n+1 に変えると、青くなるセルが 1, 4, 7, 10 へずれます。数字を書き換えて、選ばれるセルがどう変わるか試してみてください。
<div class="grid">
<div class="cell">1</div>
<div class="cell">2</div>
<div class="cell">3</div>
<div class="cell">4</div>
<div class="cell">5</div>
<div class="cell">6</div>
<div class="cell">7</div>
<div class="cell">8</div>
<div class="cell">9</div>
<div class="cell">10</div>
</div>
.grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
}
.cell {
height: 64px;
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
font-weight: bold;
color: #555;
background: #eceff4;
border-radius: 8px;
}
/* 3つおき(3, 6, 9 番目)を青で塗る */
.cell:nth-child(3n) {
background: #4a6cf7;
color: #fff;
}
/* 先頭から3つ(1, 2, 3 番目)だけ枠線を付ける */
.cell:nth-child(-n + 3) {
outline: 3px solid #f7884a;
outline-offset: -3px;
}
:nth-child と :nth-of-type の違い
このふたつは「何番目か」を数える点では同じですが、数え方が決定的に違います。:nth-child() は要素の種類を問わず、兄弟全体の中で何番目かを数えます。一方 :nth-of-type() は同じ種類(同じタグ名)の要素だけを抜き出して数えます。この差が、思った要素にスタイルが当たらない原因になりがちです。
次のような HTML を考えてみます。div の中に見出しと段落が混在しているケースです。
<div class="box">
<h2>タイトル</h2> <!-- 1番目の子。pとしては数えない -->
<p>1つ目の段落</p> <!-- 2番目の子 / pとしては1番目 -->
<p>2つ目の段落</p> <!-- 3番目の子 / pとしては2番目 -->
</div>
「最初の段落」に色を付けたくて p:nth-child(1) と書いても、何も起こりません。1 番目の子は h2 であって p ではないため、条件に一致する要素が存在しないからです。ここで p:nth-of-type(1) と書けば、p だけを数え直して「最初の段落」を正しく選べます。
/* h2が1番目なので、これは1つも一致しない */
.box p:nth-child(1) {
color: red;
}
/* pだけを数えるので、最初の段落に当たる */
.box p:nth-of-type(1) {
color: blue;
}
ざっくり言えば、対象の要素だけがきれいに並んでいる(テーブルの tr やリストの li のように兄弟が全部同じタグの)ときは :nth-child() で十分です。見出しや画像など別のタグが混ざっていて、特定のタグだけを数えたいときは :nth-of-type() を選ぶ、と覚えておくと迷いません。両者の違いを表にまとめます。
| 擬似クラス | 数える対象 | 向いている場面 |
|---|---|---|
:nth-child() | 種類を問わず兄弟全体 | 兄弟がすべて同じタグのとき |
:nth-of-type() | 同じタグの要素だけ | 別のタグが混在しているとき |
末尾から数える :nth-last-child / :nth-last-of-type
数え始める向きを後ろからにしたいときは、:nth-last-child() と :nth-last-of-type() を使います。役割はそれぞれ :nth-child() / :nth-of-type() と同じで、違いは「末尾から数える」点だけです。たとえば li:nth-last-child(1) は最後の項目、li:nth-last-child(2) は後ろから2番目を指します。an+b も同じように使えるので、:nth-last-child(odd) なら末尾を基準にした奇数番目になります。
項目数が変動するリストで「最後の2つだけ余白を変えたい」といった、後ろからの位置を基準にしたい場面で重宝します。前から数えると項目が増減するたびに番号がずれてしまいますが、後ろから数えれば末尾の位置は変わらないためです。
狙った要素に当たらないとき
数えているのは「子」であることを思い出す
:nth-child() でつまずく原因の多くは、セレクタの種類と数える順番を混同していることです。.item:nth-child(2) は「2 番目の子であり、かつ .item でもある要素」という意味で、「.item の中の2番目」ではありません。まず親の中で 2 番目の子を探し、それが .item かどうかを確かめる、という順で評価されます。2 番目の子が別のクラスだった場合、一致する要素はなくなります。
余計な要素が番号をずらしていないか
HTML 上に見出しや非表示の要素、コメント以外の空要素などが紛れていると、それらも子として数えられて番号がずれることがあります。狙った番号に当たらないときは、ブラウザの開発者ツールで親要素の子を実際に並べて確認し、対象だけを数えたいなら :nth-of-type() へ切り替えるか、対象要素を専用の親で囲んで兄弟を揃えると安定します。先ほどのテーブル例で thead と tbody を分けたのも、本文の行だけを 1 から数えさせるための工夫でした。
まとめ
:nth-child() と :nth-of-type() を使えば、HTML にクラスを足さずに「何番目か」でスタイルを当てられます。偶数・奇数のストライプには even / odd、一定間隔や範囲の指定には 3n や -n+3 のような an+b 記法が使えました。n に 0、1、2… を順に入れて計算する仕組みを押さえれば、どの番号に当たるかを自分で見積もれます。:nth-child() は兄弟全体、:nth-of-type() は同じタグだけを数えるという違いがあり、別のタグが混在するときは後者を選ぶのが安全です。末尾を基準にしたいときは :nth-last-child() / :nth-last-of-type() を組み合わせて、狙った位置を的確に指定してみてください。