フロントエンドエンジニアやコーダーが結構おざなりにしているnth-child
とnth-of-type
の違いを解説します。知人の中にも「必要になったら毎回ググってる」人が多く、あまりきちんと理解しきれてないことがよくあります。
今回の目的は、セレクタの指定でよく使うnth-child
とnth-of-type
の違いを把握して、今後ググらなくてもよいように整理しておくことです。
何番目かを指定するときにいつもnth-childかnth-of-typeか分からなくてググッちゃうんだよね
ポイントを整理しておきましょうね
nth-child、nth-of-typeとは?
CSSに限らずjQueryやJavaScriptでも利用するシーンが多いですね。「何番目の」という意味のセレクタです。
毎回毎回「あれ、どっちだったっけ」となってGoogle検索をかけることがあるのではないでしょうか。
「:nth-child(n)」とは子要素のn番目を指定する疑似クラスです。CSSやjQueryなどに頻出します。 「:nth-of-type(n)」と違うのは指定外の要素をカウントするかどうかです。
では、早速実際のサンプルコードと共にどのような挙動をするのか検証していきましょう。
nth-childとnth-of-typeの根本的な違い
次のように<div>
の中に複数の<p>
があった場合はどうなるでしょうか?
まずはp:nth-child(3)
として3番目の要素の文字色を変えてみましょう。
<div class="child-test">
<p>1番目</p>
<p>2番目</p>
<p>3番目</p>
<p>4番目</p>
<p>5番目</p>
<p>6番目</p>
<p>7番目</p>
</div>
<style>
.child-test p:nth-child(3) { /* ←ここで疑似クラスnth-childを指定 */
color: #d90368;
}
</style>
1番目
2番目
3番目
4番目
5番目
6番目
7番目
3番目の<p>
の文字色が変わりますね。実はこれ、:nth-child(3)
の代わりに:nth-of-type(3)
で指定しても同じ結果になります。
<div class="of-type-test">
<p>1番目</p>
<p>2番目</p>
<p>3番目</p>
<p>4番目</p>
<p>5番目</p>
<p>6番目</p>
<p>7番目</p>
</div>
<style>
.of-type-test p:nth-of-type(3) { /* ←ここで疑似クラスnth-of-typeを指定 */
color: #d90368;
}
</style>
1番目
2番目
3番目
4番目
5番目
6番目
7番目
この例では:nth-child(3)
と:nth-of-type(3)
に違いがないことが分かりました。
では、次のように<p>のグループの中に<div>を混ぜてみます。
<div class="child-test">
<p>1番目</p>
<div>2番目</div> <!-- ←2番目の<p>を<div>に変更しました -->
<p>3番目</p>
<p>4番目</p>
<p>5番目</p>
<p>6番目</p>
<p>7番目</p>
</div>
<style>
.child-test p:nth-child(3) { /* ←セレクタの指定は3番目の<p>の(つもりの)ままです */
color: #d90368;
}
</style>
1番目
3番目
4番目
5番目
6番目
7番目
あれ、3番目の<p>を指定しているのに実質2番目の<p>の文字の色が変わっちゃうう!?ってなりますよね。p:nth-of-type(3)
はどうでしょうか?
<div class="of-type-test">
<p>1番目</p>
<div>2番目</div> <!-- ←2番目の<p>を<div>に変更しました -->
<p>3番目</p>
<p>4番目</p>
<p>5番目</p>
<p>6番目</p>
<p>7番目</p>
</div>
<style>
.of-type-test p:nth-of-type(3) { /* ←セレクタの指定は3番目の<p>のままです */
color: #d90368;
}
</style>
1番目
3番目
4番目
5番目
6番目
7番目
p:nth-of-type(3)
の時は、4番目の要素(<div>
を含む)の文字色が変わりましたね。3番目の<p>
(<div>
をカウントしないで)の文字色がちゃんと変わりました。
おそらく皆さんが混乱するのはここですね。3番目の<p>を指定したつもりが3番目の子要素だったり4番目の子要素だったりするので訳が分からなくなるんです。
ここできちんと整理しておきましょう。nth-childとnth-of-typeの言葉の意味をつかんでおけばすんなり理解することが出来ます。
- :nth-child
- childは子要素の意味。nthはn番目のなので「n番目の子要素」。この場合<p>か<div>かは関係ない。
- :nth-of-type
- typeは型の意味。型はタグの種類を示すので、「n番目の○○タグ」を指定する。この場合、タグの種類が考慮される。
このように単語の意味を考えれば何も難しいことはありません。
nth-childとnth-of-typeでハマりがちな例
3番目の<div>
ちょっと雰囲気を変えてCSSで指定するのではなく、jQueryを使って指定してみましょう。次のようなDOM構造があると仮定します。
<div class="child-test-2">
<p>1番目の<p></p>
<div>1番目の<div></div>
<p>2番目の<p></p>
<div>2番目の<div></div>
<p>3番目の<p></p>
<div>3番目の<div>←ここの背景を青にしたい</div>
<p>4番目の<p></p>
<div>4番目の<div></div>
<p>5番目の<p></p>
<div>5番目の<div></div>
<p>6番目の小夜子<p></p>
</div>
これの3番目の<div>
の背景色を変えたいときにdiv:nth-child(3)
を指定しがちですが痛い目にあいます。
// 失敗例
$(function(){
$('.child-test-2 div:nth-child(3)').css('backgroundColor', 'blue');
});
1番目の<p>
2番目の<p>
3番目の<p>
4番目の<p>
5番目の<p>
6番目の小夜子
あれ、効かない……ってなりますよね。ですが効かなくて当然なんです。先ほど説明したのでもうお分かりでしょうが、この場合は:nth-child(n)
ではなく:nth-of-type(n)
を使うんですね。
$(function(){
$('.child-test-2 div:nth-of-type(3)').css('backgroundColor', 'blue');
});
1番目の<p>
2番目の<p>
3番目の<p>
4番目の<p>
5番目の<p>
6番目の小夜子
これで「3番目のdiv」の背景の色がちゃんと変わりました。
最後の<li>
:nth-child(n)
に類似して、:last-child
という疑似要素があるので一応おさえておきましょう。次のサンプルコードを例に進めます。今回は、<li>
が入れ子になっている場合です。
<ul class="last-child-test">
<li>1番目のli
<ul>
<li>1番目のliの中のli</li>
</ul>
</li>
<li>2番目のli
<ul>
<li>2番目のliの中のli</li>
</ul>
</li>
<li>3番目のli
<ul>
<li>3番目のliの中のli</li>
</ul>
</li>
<li>4番目のli←ここの背景色を変えたい!!
<ul>
<li>4番目のliの中のli</li>
</ul>
</li>
</ul>
$('.last-child-test li:last-child')
というセレクタの指定をしがちですね……もちろんうまく動作しません。
// 失敗例
<script>
$(function(){
$('.last-child-test li:last-child').css('backgroundColor', 'blue');
});
</script>
- 1番目のli
- 1番目のliの中のli
- 2番目のli
- 2番目のliの中のli
- 3番目のli
- 3番目のliの中のli
- 4番目のli
- 4番目のliの中のli
あらら、予期していない<li>
まで背景が青くなってしまいましたね。理由は<li>
の中に入れ子になっている<li>
にも反応してしまうようなセレクタ指定が良くないからです。
$('.last-child-test li:last-child')
という書き方では、深い階層にある余計な<li>
にまで反応してしまいます。この場合の回避策は「>
(大なり)」を間に挟んであげて、「(要素の)直下の」という指定をしてあげなくてはなりません。直下の<li>
以外にも反応してしまうので、直下の要素のみに当てる感じです。
$('.last-child-test > li:last-child')
というような具合ですね。するとうまく最後の1階層目の<li>
の最後だけ背景色が変わるようになります。デザイン的な問題は置いておいて……
- 1番目のli
- 1番目のliの中のli
- 2番目のli
- 2番目のliの中のli
- 3番目のli
- 3番目のliの中のli
- 4番目のli
- 4番目のliの中のli
Chromeのデベロッパーツールで見てみましょう。きちんと(1階層目の)最後の<li>
にstyle=""
が付与されているのが分かります。
特に何も考えずにjQueryで検証しましたが、CSSより分かりやすいかもしれませんね。class付けるとかstyle付けるとか、ぱっと見で分かるので視認性がよいです。
:first-child、:first-of-type、:last-child、last-of-typeも根本的な違いは:nth-child(n)、:nth-of-type(n)と同じ
まとめになりますが、以下の3点に気を付ければもうググらなくてよくなります。
- childは子要素の意味で、どんな種類のタグでも無視して子要素の全部の中からN番目を指定する。
- 反対にof-typeは型の意味で、指定したタグがN番目に登場した際に反応する。
- <li>や<div>の入れ子は要注意。「>(直下)」をうまく利用して対象を絞り込む。
施行を整理すれば怖いものなどほとんどありません。どんな勉強方法でも施行を整理することや、間違えた内容を反復して思い出したり、書いたりすると自然とコーディング出来るようになりますね。
では、みなさんもハマらないように要点は押さえておいてくださいね!