要素の指定にもう迷わない?:nth-child()のofフィルターを使ったラクラク要素指定法

UI開発者 板垣

みなさんは普段、CSSの:nth-child()を使用しているでしょうか?
とても便利な疑似クラスですが、仕様を把握していないと想定した要素にスタイルが当たらず、いたずらに時間を消費してしまうことも少なくないでしょう。

ですが、今回ご紹介する:nth-child()の「of」フィルターを使用すれば、そのような事態を回避できるようになるかもしれません。

:nth-child()のofフィルターとは

W3Cの公表しているSelectors Level 4で策定された表記であり、Selectors Level 3以前までは存在しなかったものとなります。

※この仕様は執筆時点では草案となっており、Safari(iOS含む)のバージョン9以上にしか対応していないことをご留意ください。
※本記事では、:nth-child()を基にofフィルターを紹介しますが、ofフィルターは:nth-last-child()などでも使用できます。
※本記事では、後述する:nth-child(An+B [of S]?)の「[of S]」の部分を便宜上「ofフィルター」と定義しています。

仕様書には:nth-child(An+B [of S]?)と記法が定められています。実際のコードで見てみると以下のようになります。

<style>
div :nth-child(1 of p) {
    color: red;
}
</style>

<div>
    <span>a<span>
    <p>b</p>
    <p>c</p>
<div>

上記を実行すると、div要素内の1番始めにあるp要素(b)の文字色が赤色になることがわかります。

ofフィルターを使わないで例と同じようにdiv要素内の1番始めにあるp要素の文字色を変更する場合は、div p:nth-child(2)と書かなくてはいけません。
もちろんこの挙動は仕様であり、なんらおかしいことはありません。しかし1番目の要素のスタイルを変えたいのに2と入力しなければならないのは、いささか混乱を生みます。
もしも、この記事を読んでいるあなたがCSSに深く精通しているのであれば:nth-of-type()を使用することで、数値を1のママにした状態で直感的に文字色を変更できることに気づき、逆にofフィルターのうまみに気づけていないのではないかと思います。

そのため、次の章では、:nth-of-type():nth-child()のofフィルターを使用した場合の違いをご紹介します。

:nth-of-type()と:nth-child()のofフィルターを使用した場合の違い

前章で触れた:nth-of-type()との違いですが、これはセレクターにクラスなどを指定した際に顕著に表れます。
まずは、以下のコードをご覧ください。

<style>
div .text:nth-of-type(1) {
    color: red;
}
</style>

<div>
    <span class="text">a<span>
    <p class="text">b</p>
    <p class="text">c</p>
<div>

一見、div要素内の1番始めにある.textクラスがついた要素(a)の文字色のみが変わるように見えますが、このコードを実行すると1番始めの要素(a)と2番目の要素(b)に色がついてしまいます(:nth-child()の時と同じく仕様です)。

深くは触れませんが、上記の例では初めにdiv要素内のspanpの2つの要素を検索の対象として取ります。
そして、それらの要素の1番目に.textクラスが付与されているかを確認し、クラスが付与されている場合にのみスタイルを適用しています。
つまり、:nth-of-type()はあくまでも要素の種類を基準にして対象を取っているということです。

となったら、:nth-of-type()も直感的かと問われればそうではない気がしてきませんか?この事実を踏まえて次の:nth-child()のofフィルターを使ったコードをご覧ください。

<style>
div :nth-child(1 of .text) {
    color: red;
}
</style>

<div>
    <span class="text">a<span>
    <p class="text">b</p>
    <p class="text">c</p>
<div>

このコードを実行すると、div要素内の1番始めにある.textクラスがついた要素(a)のみに文字色がつきます。実に直感的です。

引数について

これまでの例では、単一のセレクターのみを使用してきましたが、セレクターはcomplex-selector-listで指定できます。
ちなみに、complex-selectorというのはコンビネーターが含まれる複合セレクターを指していて、.hoge ~ pのような書き方のことです。
また、末尾に-listと書いてある通り、セレクターをカンマ区切りにすることで複数指定できます。

詳しくは仕様書をご確認いただくとして、具体的には以下のようなコードを記述できます。

<style>
div :nth-child(2n of .text, .text2 + .text2) {
    color: red;
}
</style>

<div>
    <span class="text">a<span>
    <p class="text">b</p>
    <p class="text">c</p>
    <p class="text">d</p>
    <span class="text2">e<span>
    <p class="text2">f</p>
    <p class="text2">g</p>
    <p class="text2">h</p>
    <p class="text2">i</p>
<div>

このコードを実行するとdiv要素内の偶数番目にある.textクラスがついた要素(b, d)と、div要素内の偶数番目の.text2クラスがついた要素の後方に隣接した.text2クラスがついた要素(g, i)に文字色がつきます。

おわりに

本記事では直感的に要素の指定ができることからofフィルターを持ち上げていますが、やみくもに複数のセレクターを指定していくと保守性低下の要因になりかねないと考えています。
前述した通りこの機能はまだ草案であり実用できるレベルでありませんが、複雑すぎるセレクター指定は避けて、シンプルな指定を心がけていただければと思います。