[CSS]疑似クラス:first-child、:last-child、:nth-childを活用して特定の要素にスタイルを適用しよう

ウェブサイト制作をしていると、繰り返し登場する任意の要素にスタイルを適用したい場面が多々あります。特に、リストやテーブルの特定の項目に異なるデザインを施す際に便利なのがCSSの疑似クラスです。この記事では、:first-child:last-child:nth-childの疑似クラスを活用し、特定の要素にスタイルを適用する方法を学びます。

疑似クラスとは

疑似クラスは、CSSでHTML要素に追加のクラスを付けなくても特定の条件に基づいてスタイルを適用するためのものです。これにより、HTMLを変更せずにスタイル制御が可能になります。例えば、リスト項目の最初や最後の要素に特別なスタイルを適用することができます。

疑似クラスを使うとコードが簡潔になり、HTMLとCSSの分離が保たれるため、メンテナンス性が向上します。

:first-child疑似クラス

:first-child疑似クラスは、親要素の中で最初の子要素を選択するための擬似クラスです。

.container :first-child {
  ...
}

このように書くことで、containerクラスの子要素のうち、最初の子要素を選択することができます。

:last-child疑似クラス

:last-child疑似クラスは、親要素の中で最後の子要素を選択するための擬似クラスです。

.container :last-child {
   ...
}

このように書くことで、containerクラスの子要素のうち、最後の子要素を選択することができます。

:nth-child疑似クラス

:nth-child疑似クラスは、親要素の中でn番目の子要素を選択するための擬似クラスです。nの部分には、数字やキーワード、または数式が入ります。

.container :nth-child(2) {
  ...
}

このように書くことで、containerクラスの子要素のうち、2番目の子要素を選択することができます。

:nth-last-child疑似クラス

:nth-child疑似クラスは、:nth-childの逆順で選択するための疑似クラスです。

.container :nth-last-child(2) {
  ...
}

このように書くことで、containerクラスの子要素のうち、最後から2番目の子要素を選択することができます。

:not疑似クラス

:first-child疑似クラス、:last-child疑似クラス、:nth-child疑似クラス、:nth-last-child疑似クラスの選択を否定したい場合、:not疑似クラスを使用します。

.container :not(:first-child) {
  ...
}

このように書くことで、containerクラスの子要素のうち、最初の要素以外を選択することができます。

要素の適用に対する注意点

疑似クラスの適用範囲について、気をつけておいた方がよい点が2つあります。

親要素と子要素の関係性

例えば、次のようなHTMLを考えてみます。

<div class="container">
  <div class="item">1-1</item>
  <div class="item">1-2</item>
  <div class="item">1-3</item>
</div>
<div class="container">
  <div class="item">2-1</item>
  <div class="item">2-2</item>
  <div class="item">2-3</item>
</div>
<div class="container">
  <div class="item">3-1</item>
  <div class="item">3-2</item>
  <div class="item">3-3</item>
</div>

このHTMLに対して、

.item:nth-child(odd) {
  color: red;
}

を適用すると、どの文字が赤字になるでしょうか?

実際にやってみると一目瞭然ですが、

のように親要素.containerごとに子要素.itemの奇数番目の要素が赤字になることを確認することができます。同じ子要素の指定だとしても親要素を超えて処理されないという点に注意が必要なため、誤解や誤認を避けるために

.item:nth-child(odd) {
  color: red;
}

のように子要素だけ定義するのではなく、

.container .item:nth-child(odd) {
  color: red;
}

のように、親要素 子要素:疑似クラスのように必ず親要素をつけて定義する方がよいと思います。

以降の説明でも、親要素をつけて定義する形式でスタイルを定義するようにしています。

複数種類の子要素が混在している場合

テーブルのように単一の子要素のみで構成される場合は問題ありませんが、複数種類の子要素が混在している状況では期待したとおりにスタイルが適用されない場合があります。

例えば、次のようなHTMLを考えてみます。

<div class="container">
  <h2>タイトル</h2>
  <p class="phrase">1行目</p>
  <p class="phrase">2行目</p>
  <p class="phrase">3行目</p>
  <p class="phrase">4行目</p>
  <p class="phrase">5行目</p>
</div>

phaseクラスが定義されたp要素以外にh2要素があります。

このHTMLに対して次のスタイルを適用します。

.container .phrase:nth-child(2n) {
  color: red;
}

このスタイルでは、phaseクラスが適用された要素の偶数行目の文字を赤字にします。

実際にやってみると、期待したとおりの結果になりませんでした。疑似クラスを使用したとき、その要素が何個目であるかは指定したクラス(今回はphraseクラス)の中で何個目かではなく、親要素から見たときに何個目の要素かで決まります。

すなわち、タイトルが1個目、1行目が2個目、2行目が3個目、・・・という風に数えられ、その中で指定したクラス(今回はphraseクラス)にのみスタイルが適用されます。

このことを確認するために、:first-child疑似クラスを適用してみて色が変わらないこと、:last-child疑似クラスを適用してみて色が変わることを確認しましょう。

まずは、:first-child疑似クラスを確認します。

.pattern .phrase:first-child {
  color: red;
}

:first-child疑似クラスで指しているのはタイトルで、phaseクラスが適用されていないため、色は変わりませんでした。

次に、:last-child疑似クラスを確認します。

.pattern .phrase:last-child {
  color: red;
}

:last-child疑似クラスで指しているのは4行目で、phaseクラスが適用されているため、色は変わりました。

1つの親要素の中に複数種類の子要素が混在する場合、何個目かは要素の種類に関係なく親要素から見て何個目かで数える点に留意してください。

パターン別

今回はすべて、以下のHTMLに対して適用するパターンで説明します。

<!DOCTYPE html>
<html lang="en">
  <body>
    <div class="container">
      <div class="item"></div>
      <div class="item"></div>
      <div class="item"></div>
      <div class="item"></div>
      <div class="item"></div>
    </div>
  </body>
</html>

スタイルが適用されている要素はアクアで色づけし、スタイルが適用されていない要素はグレーのままになるようにします。

最初の要素にのみ適用

最初の要素にのみ適用したい場合は、:first-child疑似クラスを使用します。

.container .item:first-child {
  background-color: aqua;
}

最後の要素にのみ適用

最後の要素にのみスタイルを適用したい場合は、:last-child疑似クラスを使用します。

.container .item:last-child {
  background-color: aqua;
}

最初と最後の要素に適用

:first-child疑似クラスと:last-child疑似クラスを組み合わせることで、最初と最後のようにスタイルを適用することができます。

.container .item:first-child,
.container .item:last-child {
  background-color: aqua;
}

最初の要素以外に適用

最初の要素以外にスタイルを適用したい場合、:first-child疑似クラスとそれを否定する:not疑似クラスを組み合わせることで実現できます。

.container .item:not(:first-child) {
  background-color: aqua;
}

最後の要素以外に適用

最後の要素以外にスタイルを適用したい場合は、:last-child疑似クラスとそれを否定する:not疑似クラスを組み合わせることで実現できます。

.container .item:not(:last-child) {
  background-color: aqua;
}

最初と最後の要素以外に適用

最初と最後の要素以外にスタイルを適用したい場合、:first-child疑似クラス、:last-child疑似クラス、:not疑似クラスを組み合わせて実現します。こちらは最初の要素以外 かつ 最後の要素以外というAND条件になりますので、OR条件の場合とは書き方が異なります。

.container .item:not(:first-child):not(:last-child) {
  background-color: aqua;
}

偶数個目の要素に適用

テーブルの行をストライプにするときによく使われるのが偶数個目または奇数個目の要素にスタイルを適用する方法です。これには:nth-child疑似クラスを使用します。この書き方には2つあるのでそれぞれ説明します。

まずはnを使って書く方法です。nを使って偶数をあらわすためには2nと書きます。

.container .item:nth-child(2n) {
  background-color: aqua;
}

もう少し簡単に書く方法があります。偶数をあらわすevenを使います。

.container .item:nth-child(even) {
  background-color: aqua;
}

奇数個目の要素に適用

奇数個目の要素に適用する方法も2つあります。

まずはnを使って書く方法です。nを使って奇数をあらわすには2n+1と書きます。

.container .item:nth-child(2n+1) {
  background-color: aqua;
}

偶数のときと同じように簡単に書く方法があります。奇数をあらわすoddを使います。

.container .item:nth-child(odd) {
  background-color: aqua;
}

特定の要素に適用

:nth-child疑似クラスを使うことで特定の要素にスタイルを適用することができます。

一例として、3個目の要素にのみ適用する場合を考えてみます。:nth-child疑似クラスに3を指定します。

.container .item:nth-child(3) {
  background-color: aqua;
}

ただし、:nth-child疑似クラスは万能ではありません。書けないケースや書き方に工夫が必要なケースもあります。

以下のようなことは:nth-child疑似クラスで実現可能です。

  1. 特定の順序の要素を選択
    • :nth-child(2)は、親の中で2番目に位置する子要素を選択します
  2. 特定のパターンに基づく要素を選択
    • :nth-child(odd)または:nth-child(2n+1)は、奇数番目の要素を選択します
    • :nth-child(even)または:nth-child(2n)は、偶数番目の要素を選択します
    • :nth-child(3n)は、3番目、6番目、9番目など、3の倍数の位置にある要素を選択します
  3. 複雑なパターンで要素を選択
    • :nth-child(3n+1)は、1番目、4番目、7番目など、3つおきに要素を選択します
    • :nth-child(-n+3)は、最初の3つの要素(1番目、2番目、3番目)を選択します
  4. 他の疑似クラスとの組み合わせ
    • :nth-child(2):hoverのように、特定の順序にある要素がホバーされたときのスタイルを指定できます
  5. 特定のタグに対して使用
    • p:nth-child(3)のように、特定のタグ(例えばp要素)が兄弟要素の中で指定された順序に位置する場合にスタイルを適用できます

一方で以下のようなことは:nth-child疑似クラスではで実現できません。

  1. 特定のクラスや属性を持つ要素を選択
    • 前述の注意点でも挙げたとおり、:nth-child疑似クラスは要素の順序に基づいているため、クラス名や属性を無視します。例えば、p.special:nth-child(2)のように、特定のクラスを持つ2番目の要素を選択することはできません
  2. 逆順の選択
    • :nth-child疑似クラスは要素の逆順での選択はできません。例えば、末尾から2番目の要素を選択することはできません。ただし、:nth-last-child疑似クラスを使用することで実現可能です
  3. 特定の要素タイプに対してだけの選択
    • :nth-child疑似クラスは指定された順序にあるすべての子要素を対象とするため、特定の要素タイプだけを選択することはできません。例えば、特定の順序にある div要素だけを選択することはできません。div:nth-child(2)は、2番目の子要素がdiv要素である場合にのみ適用されます
  4. an+bの形式であらわせない要素を選択
    • :nth-child疑似要素では、2などの特定値での指定、evenまたはoddan+bの形式(abは省略可)のいずれかの方法でしか指定できません。そのため、可変個数の要素のちょうど真ん中の要素に対してスタイルを適用するということはできません

an+bの形式であらわせない形式であっても工夫によって表現可能な場合があります。例えば、1番目と4番目の要素に対してスタイルを適用したい場合、

.container .item:nth-child(1,4) {
  background-color: aqua;
}

とは書けませんが、

.container .item:nth-child(1),
.container .item:nth-child(4) {
  background-color: aqua;
}

と書くことで実現できます。

まとめ

:first-child疑似クラス、:last-child疑似クラス、:nth-child疑似クラス(または:nth-last-child疑似クラス)および:not疑似クラスを使用することで、一部制約はあるものの様々要素を指定することができるようになります。

テーブルをストライプ模様にする以外にも、最初と最後の要素以外にマージンを設けたり、最初の要素だけデザインを変えたりなど、工夫によって様々なことができるようになります。

指定方法には少し注意すべき点がありますが、活用することでデザインの幅はさらに広がることでしょう。

テキストの改行処理は多くの場合デフォルトでいいかもしれない

ここ最近、テキストが折り返されずにはみ出るとかテキストが折り返されるがエリアからはみ出るといった不具合に関わることが多かったので、少し調べてみました。

結論

比較検証を色々してもよかったのですが、結論がかなりシンプルなので結論だけ書いていきます。

基本的にはoverflow-wrapword-breakは不要

多くのケースではoverflow-wrapword-breakによる改行処理は不要だと思います。親要素で幅を制限し、データ量に応じて高さが変わるようになっているのであれば、自然に改行してくれます。

対象が文章であっても禁則処理まで求められるケースは多くない印象です。

テストデータやダミーデータには注意が必要

abc...」のような英字の羅列は1つの単語と見なして途中では折り返されません。テスト時やダミーデータを登録する場合などにこのような文字列を設定しがちですが、想定外の文字列を設定しているのであればバグではないため、設定する文字列を見直しましょう。

コードやIDには注意

コードやIDが固定長であれば折り返さずに表示できる幅を確保するようにデザインすることを検討してください。コードやIDの視認性を考えると折り返さない方が読みやすくなります。

一方で、コードやIDが可変長であったり、複数のコードやIDが同じエリア内に混在するような場合、一部のコードやIDが想定した幅を超えることがあります。このような場合、折り返しを実現する方法として、word-break: break-allの使用を検討してください。break-allを指定した場合、単語の途中でも折り返しますが、コードやIDであれば機械的に端で折り返す方が適していると思います。

複数のシステムと関連していたり、歴史的な理由でコードなのに可変ということは結構あります。現行システムのデータなどを分析して複数の桁のデータが存在していることが確認できた場合は、何桁までを折り返さずに表示できるようにするとよいのか確認した方がよいと思います。

利用者の特性によっては一部の項目にword-break: break-allの使用を検討する

利用者によっては一部の項目にword-break: break-allの使用を検討しなければならないかもしれません。業種、企業風土、利用者のWebリテラシーなど様々な要因によって決まるため一概には言えませんが、主に備考欄や特記事項欄のようなフリー入力欄ではテストデータやダミーデータで入力されがちな自然に折り返すことができないテキストを入力する場合がありますので、word-break: break-allの使用可否を検討することをおすすめします。

テキストの省略には注意が必要

すべての文字がエリア内に収まらない場合、CSSを使って「...」で省略することができます。ただし、省略することで区別不可能になる場合があるため、使いどころには注意が必要です。

例えば、「東京都中央区第一支社」「東京都中央区第二支社」という文字列がある場合に、これを幅の問題で「東京都中央区第…」のように省略しなくてはならないケースを考えてみます。この場合、省略されていると「第一支社」なのか「第二支社」なのか区別がつきません。テキストにマウスカーソルを当てるとすべてのテキストがツールチップで表示する場合もありますが、想定内の操作をしているだけで、マウスカーソルを当てるという余計な操作を余儀なくされるのは、利用者にとっては有益なことではないと思います。

省略する場合、JavaScriptなどで「東京都…第一支社」のように省略する方法も検討した方がよいかもしれません。

まとめ

テキストが折り返されずにエリアを貫通する問題はほとんど問題にならないと考えてよさそうですが、テスト時やダミーデータ設定時には正しくデータを想定して設定することが重要です。

また、一部の状況においてはword-break: break-allの使用を検討する必要がありますが、とりあえず設定するのではなく、十分検討した上で設定するようにするとよいでしょう。

モバイルバージョンを終了