nth-child擬似要素のちょっと意外な(?)使い方

先日、とある案件でこのようなメニューを組むことがありました。

これが意外に苦戦をしましたので、その解決法を残しておこうと思います。

はじめにやったこと

まず、2カラムのメニューを作るためのcssを記述しました。

ul{
  display: flex;
  flex-wrap: wrap;
}
li{
  width: 50%;
  border-bottom: 1px solid #999;
}
li:nth-last-child(-n+2){
 border-bottom: 0;
}
a{
  display: block;
  padding: 8px 10px;
}

これでメニューの形ができます。

最後から2番目までの要素の下ボーダーを消すために:nth-last-child(-n+2)という風にしています。

demo

しかしこれだと2つ目のメニューで不具合があります。下から2段目の右側の要素の下ボーダーがありません。

最後から2番目までの下ボーダーを消すという処理をしたため、要素の数が奇数の場合、下から2段目の右側の要素も下ボーダーが消えてしまうのです。

解決するためにやったこと

なんとかして要素の数が偶数の時と奇数の時でスタイルを分けれないかと思っていた時、以前、複数のnth-child系の擬似要素を組み合わせて要素の個数によってスタイルを変えることのできる書き方を聞いたことを思い出しました。

ということでこのように書き換えました。

ul{
  display: flex;
  flex-wrap: wrap;
}
li{
  width: 50%;
  border-bottom: 1px solid #999;
}
li:nth-child(odd):last-child,
li:nth-child(odd):nth-last-child(2),
li:nth-child(even):last-child{
  border-bottom: 0;
}
a{
  display: block;
  padding: 8px 10px;
}

するとすべての要素に下ボーダーが表示されます。

demo

どういうことをしているかというと、

  • :nth-child(odd):last-child・・・奇数番目かつ最後の要素(カテゴリー2のリスト5)
  • :nth-child(odd):nth-last-child(2)・・・奇数番目かつ最後から2番目の要素(カテゴリー1のリスト7)
  • :nth-child(even):last-child・・・偶数番目かつ最後の要素(カテゴリー1のリスト8)

をそれぞれ指定しています。

こうすることで最初のdemoで下ボーダーのなかったカテゴリー2のリスト4の位置に当たるような要素には影響しないようにスタイルを付けることができるようになります。

まとめ

nth-child系の擬似要素は最初の要素や最後の要素を指定するのに使うことが多いかと思いますが、組み合わせることで条件の絞り込みのような使い方ができます。
これ以外でも使えるやり方があるかと思いますので、いろいろ試してみるとおもしろそうだと思います。

CSSでどうにかできるということを知らないと、こういったスタイルは要素ごとにクラスをつけることで対処することになると思いますが、そうするとその度にクラス名をつけたり、更新時にクラスを付け外ししたりしないといけなかったりして効率が悪くなります。
そもそもCMSだとクラスをつけることもできないですしね。

不要なクラスをつけることで発生する可読性や更新性の悪いマークアップを避けるためにもこういったテクニックを活用していくのがよいと思います。