CSS 最後の要素にスタイルを適用する方法(`:last-child` `:last-of-type`)


CSSで最後の要素を操る!:last-child:last-of-typeの徹底解説と実践ガイド

ウェブデザインにおいて、リストの最後の項目に区切り線を表示しない、テーブルの最終行の背景色を変える、連続する要素の最後に特定のスタイルを適用するなど、特定のグループの「最後の要素」に対してだけスタイルを適用したいというニーズは頻繁に発生します。

CSSには、このような「最後の要素」を効率的かつ柔軟に指定するための強力なセレクターが存在します。それが:last-child:last-of-typeです。これらは見た目は似ていますが、その振る舞いには重要な違いがあり、使い分けを理解することがCSSを効果的に記述する上で非常に重要です。

この記事では、:last-child:last-of-typeの基本的な使い方から、その違い、多様なHTML構造における挙動、実践的な応用例、さらには関連するセレクターや注意点まで、網羅的かつ詳細に解説します。この記事を読むことで、あなたはこれらのセレクターを自在に操り、より洗練されたCSSコーディングを実現できるようになるでしょう。

はじめに:なぜ「最後の要素」にスタイルを適用するのか?

ウェブサイトのUIデザインでは、要素のリストやグループにおいて、最初の要素、途中の要素、そして最後の要素で異なるスタイルを適用することがよくあります。例えば:

  • リスト (ul, ol, navなど): 各リストアイテム(li)の下に区切り線を入れる際に、最後のアイテムにだけ区切り線を付けたくない。
  • テーブル (table): 最後の行(tr)に特別な背景色や太字のスタイルを適用して合計行などを示す。
  • フォーム要素: 一連の入力フィールドやボタンの中で、最後の要素の右側にマージンを付けない。
  • カードリスト: グリッドレイアウトなどで表示されたカード要素の最後の要素に、特別なボーダーやシャドウを適用する。
  • 段落 (p) や見出し (h): 連続して現れる段落や見出しの中で、最後のものだけに下のマージンを減らす。

これらのデザイン要件を満たすために、以前はJavaScriptを使ったり、CSSクラスをHTMLに直接追加したりする方法が取られることもありました。しかし、:last-child:last-of-typeといったCSSの擬似クラスセレクターを使えば、HTML構造を変更することなく、純粋なCSSだけでこれを実現できます。これにより、コードの保守性が向上し、HTMLとCSSの役割分担がより明確になります。

CSSセレクターの基本:擬似クラスとは?

:last-child:last-of-typeは、CSSの擬似クラス (Pseudo-class) と呼ばれるセレクターの一種です。擬似クラスは、要素の特定の状態(例えば、:hoverで要素にマウスカーソルが乗っている状態)や、特定の構造的な関係性(例えば、:first-childで最初の子要素である状態)に基づいて要素を選択するために使用されます。

要素のクラス名やID、タグ名といった「静的な情報」だけでなく、「動的な状態」や「構造的な位置」に基づいてスタイルを適用できるのが擬似クラスの強力な点です。

:last-child:last-of-typeは、要素の「構造的な位置」に関する擬似クラスです。具体的には、親要素から見て、その要素が「最後の子であるかどうか」や、「同じタイプの兄弟要素の中で最後であるかどうか」を判定基準とします。

では、それぞれのセレクターについて詳しく見ていきましょう。

:last-child セレクターの詳細

定義と役割

:last-child擬似クラスは、親要素を持つ要素のうち、その親要素のすべての子要素 (要素ノード) の中で最後に位置する要素を選択します。

重要な点は、:last-childが考慮するのは親要素の直下にあるすべての子要素であり、セレクターで指定した要素タイプ(タグ名)に関わらず、物理的に最後に位置する要素にマッチするかどうかを判定するということです。

例えば、li:last-childというセレクターがあった場合、これは「親要素(例えばulol)の直下にあるすべての子要素の中で最後に位置する要素であり、かつその要素がliであるもの」を選択します。もし親要素の最後の子がli以外の要素(例えばpdiv)だった場合、その親の最後の子はliではないため、li:last-childは何も選択しません。

基本的な使い方(単一の親要素内の子要素)

最も一般的な使い方は、リスト (ulol) の中の最後のリストアイテム (li) を選択する場合です。

HTML:

“`html

  • リストアイテム 1
  • リストアイテム 2
  • リストアイテム 3
  • アイテム A
  • アイテム B
  • これはリスト外の段落

  • アイテム C

“`

CSS:

“`css
/ .list-last-child内の最後のliにスタイルを適用 /
.list-last-child li:last-child {
border-bottom: none; / 最後の要素には境界線を付けない /
font-weight: bold;
color: blue;
}

/ .list-last-child-mixed内の最後のliにスタイルを適用しようとする /
.list-last-child-mixed li:last-child {
color: red; / このスタイルは適用されるか? /
}

/ .list-last-child-mixed内の最後の子にスタイルを適用 /
.list-last-child-mixed :last-child {
background-color: yellow; / 最後の子要素に背景色 /
}
“`

解説:

  1. .list-last-child li:last-child: 最初の例では、ulの子要素はすべてliです。最後のliは「リストアイテム 3」です。このliは親要素(ul)の直下にあるすべての子要素の中で物理的に最後に位置する要素であり、かつliであるため、:last-childにマッチします。したがって、このセレクターは「リストアイテム 3」を選択し、青色で太字になります。border-bottom: none;も適用されます。
  2. .list-last-child-mixed li:last-child: 2番目の例では、ulの子要素はlilipliの順に並んでいます。物理的に最後の子要素は<li>アイテム C</li>ではありません。最後の子要素は<p>これはリスト外の段落</p>です。li:last-childセレクターは、「親の子要素の中で最後に位置する要素であり、かつその要素がliであるもの」を探します。しかし、最後の子要素はpであるため、liではありません。したがって、このセレクターは何も選択しません<li>アイテム C</li>には赤色が適用されません。
  3. .list-last-child-mixed :last-child: このセレクターは、.list-last-child-mixedの子孫要素の中で、それぞれが属する親要素の最後の子であるもの全てを選択します。この場合、ulの子要素の中で最後の子である<p>これはリスト外の段落</p>が選択され、背景色が黄色になります。また、lipの中にさらに子要素があれば、その最後の子も選択されますが、この例では子要素を持たないため、pだけが対象です。:last-childは要素の種類を問わず、単に最後の子であるかを判定するため、このようにpが選択されます。

この例から分かるように、:last-childはセレクターで指定した要素タイプ(例:li)が「親の最後の子」でなければマッチしません。これは、:last-childの最も重要な特性であり、しばしば意図しない挙動の原因となる点です。

様々なHTML構造での例

:last-childの挙動をさらに理解するために、様々なHTML構造で試してみましょう。

例1:div内の混在要素

“`html

タイトル

最初の段落

次の段落

これはスパン要素

最後の段落

“`

“`css
.container-last-child p:last-child {
background-color: lightgreen; / このスタイルは適用されるか? /
}

.container-last-child :last-child {
border: 2px solid orange; / 最後の子要素全てに適用 /
}
“`

解説:

  • .container-last-child p:last-child: .container-last-childの子要素は、h2, p, p, span, p の順です。最後の子要素は、物理的に最後の<p>最後の段落</p>です。この要素は親要素の最後の子であり、かつp要素であるため、p:last-childにマッチします。背景色が緑色になります。
  • .container-last-child :last-child: .container-last-childの直下の子要素の中で最後の子である<p>最後の段落</p>が選択され、オレンジ色のボーダーが付きます。もしこれらの子要素の中にさらに子要素があれば、その子要素の中で最後の子にもこのスタイルが適用されます(この例では子要素を持たないため、pだけが対象です)。

例2:テーブル構造

“`html

ヘッダー 1 ヘッダー 2
データ 1-1 データ 1-2
データ 2-1 データ 2-2
フッター 1 フッター 2

“`

“`css
.table-last-child tr:last-child {
background-color: #f0f0f0; / 最後の行に適用されるか? /
}

.table-last-child tbody tr:last-child {
background-color: yellow; / tbody内の最後の行に適用されるか? /
}

.table-last-child td:last-child {
border-right: none; / 各行の最後のセルに適用 /
}
“`

解説:

  • .table-last-child tr:last-child: table要素の子要素はthead, tbody, tfootです。trはこれらの子要素の中にさらにネストされています。tr:last-childセレクターは、tableの直下の子要素の中の最後の要素であり、かつそれがtrであるものを探しますが、tableの直下にはtrはありません。したがって、このセレクターは何も選択しません。灰色の背景色は適用されません。
  • .table-last-child tbody tr:last-child: これはtbody要素の子要素の中の最後のtrを選択します。tbodyの子要素は<tr>...</tr>が2つです。その中で最後のtrは「データ 2-1」「データ 2-2」を含む行です。このtrtbodyの最後の子であり、かつtrであるため:last-childにマッチします。黄色の背景色が適用されます。
  • .table-last-child td:last-child: これは各tr要素の子要素の中の最後のtdを選択します。各行には2つのtdがあります。それぞれのtrの中で最後のtd(例えば最初の行の「データ 1-2」、2番目の行の「データ 2-2」)が選択され、右ボーダーがなくなります。これは期待通りの挙動です。

この例から、:last-childはセレクターの対象となる要素(例: tr)が直接の親要素の最後の子であるかを判定することが重要だと分かります。テーブル構造のようにネストが深い場合、どのレベルの「最後の子」を指定しているのかを明確にする必要があります。

:last-childの注意点:要素の種類に関わらず「最後の子」を選ぶ

:last-childの最大のポイントは、「親要素のすべての子要素の中で最後に位置するか」という条件判定において、セレクターで指定した要素タイプ(例: li, p, divなど)に関わらず、その親の物理的な最後の子であれば何でもカウントするという点です。そして、その物理的な最後の子が、セレクターで指定した要素タイプと一致した場合にのみスタイルが適用されます。

もし親の物理的な最後の子が、セレクターで指定したタイプと異なる場合、:last-childセレクターはマッチしません。これが最初のリストの例でli:last-child<p>要素がある場合に最後のliにマッチしなかった理由です。

まとめると、:last-childは以下のように解釈されます:

要素E(例えばli)は、親要素P(例えばul)の最後の子要素ですか? もしそうであれば、その要素Eを選択します。」

この「最後の子要素」の判定は、親Pの直下にあるすべての種類の要素ノードを対象に行われます。(※厳密にはコメントノードやテキストノードは通常の子要素カウントには含まれませんが、ここでは視覚的な要素ノードに絞って考えています。)

:last-of-type セレクターの詳細

定義と役割

:last-of-type擬似クラスは、親要素を持つ要素のうち、その親要素の同じ要素タイプの兄弟要素 の中で最後に位置する要素を選択します。

重要な点は、:last-of-typeが考慮するのは親要素の直下にある、セレクターで指定した要素タイプと同じ兄弟要素だけであるということです。他の種類の要素が間に挟まっていても、同じタイプの要素の中での位置だけを見て判定します。

例えば、li:last-of-typeというセレクターがあった場合、これは「親要素(例えばulol)の直下にある子要素のうち、li要素だけを抜き出して並べたときに最後に位置するli要素」を選択します。他の要素タイプ(p, div, spanなど)は判定に関係ありません。

基本的な使い方(単一の親要素内の特定タイプの最後)

再びリストの例で考えてみましょう。今回は、p要素が間に挟まっているリストを使います。

HTML:

“`html

  • アイテム A
  • アイテム B
  • これはリスト外の段落

  • アイテム C

“`

CSS:

“`css
/ .list-last-of-type内のli要素の中で最後のliにスタイルを適用 /
.list-last-of-type li:last-of-type {
color: green; / このスタイルは適用されるか? /
font-weight: bold;
}

/ .list-last-of-type内のp要素の中で最後のpにスタイルを適用 /
.list-last-of-type p:last-of-type {
font-style: italic;
text-decoration: underline;
}

/ .list-last-of-type内の最後の子要素にスタイルを適用 (比較用) /
.list-last-of-type :last-child {
border: 2px dashed purple;
}
“`

解説:

  1. .list-last-of-type li:last-of-type: .list-last-of-typeの子要素のうち、li要素だけを抜き出すと「アイテム A」「アイテム B」「アイテム C」の3つです。これらのli要素の中で最後に位置するのは<li>アイテム C</li>です。この要素は親要素の直下にあるli要素の中で最後に位置するため、li:last-of-typeにマッチします。したがって、「アイテム C」は緑色で太字になります。
  2. .list-last-of-type p:last-of-type: .list-last-of-typeの子要素のうち、p要素だけを抜き出すと<p>これはリスト外の段落</p>の1つだけです。このp要素は、p要素のグループの中では当然最後に位置します。したがって、p:last-of-typeにマッチし、イタリック体で下線が付きます。
  3. .list-last-of-type :last-child (比較用): これは先ほどの例と同じで、親要素の直下にあるすべての子要素の中で最後に位置する要素を選択します。物理的な最後の子要素は<p>これはリスト外の段落</p>です。したがって、このp要素に紫色の点線ボーダーが付きます。

この例から分かるように、:last-of-type同じ要素タイプの兄弟要素の中での最後の要素を選択します。間に他の要素が挟まっていても、同じタイプの最後の要素であればマッチします。これが:last-childとの決定的な違いです。

様々なHTML構造での例

:last-of-typeの挙動をさらに理解するために、様々なHTML構造で試してみましょう。

例1:div内の混在要素(:last-childと比較)

“`html

タイトル

最初の段落

次の段落

これはスパン要素

最後の段落

“`

“`css
/ last-of-type /
.container-last-of-type p:last-of-type {
background-color: lightblue; / pタイプの中で最後 /
}

.container-last-of-type span:last-of-type {
font-size: 1.2em; / spanタイプの中で最後 /
}

/ last-child (比較用) /
.container-last-of-type p:last-child {
color: red; / pタイプで、かつ最後の子 /
}
“`

解説:

  • .container-last-of-type p:last-of-type: .container-last-of-typeの子要素のうち、p要素は3つあります(最初の段落、次の段落、最後の段落)。これらのp要素の中で最後に位置するのは<p>最後の段落</p>です。したがって、この要素に水色の背景色が付きます。
  • .container-last-of-type span:last-of-type: .container-last-of-typeの子要素のうち、span要素は1つだけです(これはスパン要素)。このspan要素はspan要素のグループの中では最後に位置するため、サイズが1.2emになります。
  • .container-last-of-type p:last-child (比較用): .container-last-of-typeすべての子要素の中で最後に位置するのは<p>最後の段落</p>です。この要素は親の最後の子であり、かつp要素でもあるため、p:last-childにマッチします。したがって、この要素は赤色になります。(つまり、<p>最後の段落</p>には、:last-of-typeによる背景色と、:last-childによる文字色の両方が適用されます。)

例2:テーブル構造(:last-childと比較)

“`html

ヘッダー 1 ヘッダー 2
データ 1-1 データ 1-2
データ 2-1 データ 2-2
フッター 1 フッター 2

“`

“`css
/ last-of-type /
.table-last-of-type tr:last-of-type {
border-bottom: 3px solid black; / trタイプの中で最後 /
}

.table-last-of-type td:last-of-type {
color: blue; / tdタイプの中で最後 /
}

/ last-child (比較用) /
.table-last-of-type tbody tr:last-child {
background-color: yellow; / tbody内の最後の子であるtr /
}
“`

解説:

  • .table-last-of-type tr:last-of-type: table要素の直下の子要素はthead, tbody, tfootです。tr要素はこれらの要素の中にネストされています。tr:last-of-typeセレクターは、親要素の直下にあるtr要素の中で最後のものを探します。しかし、tableの直下にはtr要素は存在しません。したがって、このセレクターは何も選択しません。太い下線はテーブル全体には適用されません。
    • 補足: テーブルの場合は、trが属する親要素(thead, tbody, tfoot)を明確に指定する必要があります。例えば、.table-last-of-type tbody tr:last-of-typeならtbody内の最後のtr.table-last-of-type tfoot tr:last-of-typeならtfoot内の最後のtrを選択します。この例では、tbody内の最後のtr(「データ 2-1」「データ 2-2」の行)にスタイルを適用したいことが多いでしょう。その場合は.table-last-of-type tbody tr:last-of-typeと記述します。(例のCSSには意図的に.table-last-of-type tr:last-of-typeと記述しており、これがテーブル全体には適用されないことを示しています。)
  • .table-last-of-type td:last-of-type: これは各tr要素の子要素であるtd要素の中で最後のものを選択します。各行には2つのtdがあり、それぞれの行で2番目のtd:last-of-typeにマッチします(例えば「データ 1-2」、「データ 2-2」、「フッター 2」)。これらのセルが青色になります。
  • .table-last-of-type tbody tr:last-child (比較用): これはtbodyの子要素の中で物理的に最後の子であり、かつtrである要素を選択します。この例では、tbodyの最後の子はtrであり、かつtbody内の最後のtrでもあるため、:last-child:last-of-typeは同じ要素を選択します(「データ 2-1」「データ 2-2」の行)。黄色の背景色が適用されます。

この例から、:last-of-type:last-childと同様に、セレクターの対象となる要素(例: tr)が直接の親要素の直下にある「同じタイプの兄弟要素」の中で最後の要素であるかを判定することが重要だと分かります。テーブル構造のようなネストが深い場合、どのレベルの「最後」を指定しているのかを明確にする必要があります。

まとめると、:last-of-typeは以下のように解釈されます:

「要素E(例えばli)は、親要素P(例えばul)の直下にある子要素のうち、要素Eと同じ要素タイプ(例えばli)の兄弟要素だけを抜き出して並べたときに最後に位置する要素ですか? もしそうであれば、その要素Eを選択します。」

この「最後に位置する」の判定は、親Pの直下にある子要素のうち、セレクターで指定した要素タイプと同じ種類のものだけを対象に行われます。

徹底比較::last-child vs :last-of-type

ここまで見てきたように、:last-child:last-of-typeの最も大きな違いは、「最後の要素」を判定する際に、兄弟要素としてどの範囲を考慮するかという点です。

  • :last-child: 親要素の直下にあるすべての子要素の中で物理的に最後の要素。
    • 例: p, p, span, p. ここでspanが最後の子なら、p:last-childは最後のpにはマッチしない。
  • :last-of-type: 親要素の直下にある、同じ要素タイプの兄弟要素の中で物理的に最後の要素。
    • 例: p, p, span, p. ここでpタイプの中で最後の子は最後のpp:last-of-typeは最後のpにマッチする。

この違いをさらに明確にするために、いくつかの具体的なHTML構造に対して両方のセレクターを適用し、その結果を比較してみましょう。

比較例1:異なる種類の要素が混在する場合

HTML:

“`html

最初の段落

見出し

中央の段落

スパン要素

最後の段落

“`

CSS:

“`css
/ last-child /
.comparison-container p:last-child {
border: 2px solid red; / このpは親の最後の子か? /
}

.comparison-container span:last-child {
background-color: pink; / このspanは親の最後の子か? /
}

/ last-of-type /
.comparison-container p:last-of-type {
border: 2px solid blue; / このpはpタイプの中で最後か? /
}

.comparison-container span:last-of-type {
background-color: lightblue; / このspanはspanタイプの中で最後か? /
}
“`

解説:

.comparison-containerの子要素は、p, h2, p, span, p の順です。物理的に最後の子は<p>最後の段落</p>です。

  • .comparison-container p:last-child: 親の最後の子は<p>最後の段落</p>であり、これはp要素です。したがって、このセレクターは<p>最後の段落</p>にマッチします。赤いボーダーが付きます。
  • .comparison-container span:last-child: 親の最後の子は<p>最後の段落</p>であり、これはspan要素ではありません。したがって、このセレクターは何も選択しません。ピンクの背景色はつきません。
  • .comparison-container p:last-of-type: 親の子要素のうち、p要素だけを抜き出すと「最初の段落」「中央の段落」「最後の段落」の3つです。この中で最後のp<p>最後の段落</p>です。したがって、このセレクターは<p>最後の段落</p>にマッチします。青いボーダーが付きます。(赤いボーダーと青いボーダーが両方つくことになります)
  • .comparison-container span:last-of-type: 親の子要素のうち、span要素だけを抜き出すと「スパン要素」の1つだけです。このspanはこのタイプの中で最後です。したがって、このセレクターは<span>スパン要素</span>にマッチします。水色の背景色が付きます。

この例から、:last-childは「物理的な最後の子」が指定タイプと一致するかを見るのに対し、:last-of-typeは「指定タイプの中で最後のもの」を見るという違いが明確になります。

比較例2:リストの間に他の要素がある場合

HTML:

“`html

  • Apple
  • Banana

リストの間に段落

  • Cherry
  • Date

もう一つの段落

“`

CSS:

“`css
/ last-child /
.list-comparison ul:last-child {
border: 2px solid red; / このulは親の最後の子か? /
}

.list-comparison p:last-child {
background-color: pink; / このpは親の最後の子か? /
}

.list-comparison li:last-child {
font-weight: bold; / このliは親(ul)の最後の子か? /
}

/ last-of-type /
.list-comparison ul:last-of-type {
border: 2px solid blue; / このulはulタイプの中で最後か? /
}

.list-comparison p:last-of-type {
background-color: lightblue; / このpはpタイプの中で最後か? /
}

.list-comparison li:last-of-type {
font-style: italic; / このliは親(ul)のliタイプの中で最後か? /
}
“`

解説:

.list-comparisonの子要素は、ul, p, ul, p の順です。物理的な最後の子は<p>もう一つの段落</p>です。

  • .list-comparison ul:last-child: 親の最後の子は<p>もう一つの段落</p>であり、ulではありません。したがって、このセレクターは何も選択しません。赤いボーダーはつきません。
  • .list-comparison p:last-child: 親の最後の子は<p>もう一つの段落</p>であり、これはp要素です。したがって、このセレクターは<p>もう一つの段落</p>にマッチします。ピンクの背景色が付きます。
  • .list-comparison li:last-child: これはulの子要素であるliが、その直接の親(ul)の最後の子であるかを見ます。最初のulの最後の子は<li>Banana</li>です。2番目のulの最後の子は<li>Date</li>です。どちらのliもそれぞれの親ulの最後の子であるため、両方にマッチします。両方の「Banana」と「Date」が太字になります。
  • .list-comparison ul:last-of-type: 親の子要素のうち、ul要素だけを抜き出すと2つあります(Apple/Bananaを含むul、Cherry/Dateを含むul)。この中で最後のulはCherry/Dateを含むulです。したがって、このセレクターはCherry/Dateを含むulにマッチします。青いボーダーが付きます。
  • .list-comparison p:last-of-type: 親の子要素のうち、p要素だけを抜き出すと2つあります(「リストの間に段落」、「もう一つの段落」)。この中で最後のp<p>もう一つの段落</p>です。したがって、このセレクターは<p>もう一つの段落</p>にマッチします。水色の背景色が付きます。(ピンクの背景色と水色の背景色の両方がつくことになります)
  • .list-comparison li:last-of-type: これはulの子要素であるliが、その直接の親(ul)の中で同じタイプ(li)の兄弟要素の中で最後であるかを見ます。最初のulli要素は「Apple」「Banana」です。この中で最後のliは「Banana」です。2番目のulli要素は「Cherry」「Date」です。この中で最後のliは「Date」です。したがって、両方の「Banana」と「Date」がイタリック体になります。(太字とイタリック体の両方がつくことになります)

この比較から、:last-child:last-of-typeは、特に兄弟要素に異なるタイプの要素が混在する場合に、マッチする要素が異なることが分かります。どちらのセレクターを使うかは、あなたが「物理的な最後の子要素」にスタイルを適用したいのか、それとも「特定の要素タイプの中で最後の要素」にスタイルを適用したいのかによって決まります。

どちらを選ぶべきか?判断の基準

  • :last-childを選ぶべきケース:
    • 親要素内の子要素がすべて同じタイプであることが保証されている場合 (例: 純粋な ul > liol > liselect > option など)。
    • 親要素内の子要素に異なるタイプが混在していても物理的に最後の要素にスタイルを適用したい場合。例えば、特定のコンテナ内の最後の要素に「bottom-padding」を適用したいが、その要素がpであろうとdivであろうと構わない場合など。
  • :last-of-typeを選ぶべきケース:
    • 親要素内の子要素に異なるタイプが混在しており特定の要素タイプの中から最後の要素にスタイルを適用したい場合。例えば、divの中に複数のh2pがあり、最後のpだけにスタイルを適用したいが、そのpの後にspanがあっても構わない場合など。リストの間に広告や区切り線のような他の要素が入る可能性がある場合、リストアイテムの最後の要素を選択するのに便利です。

多くの場合、リストアイテムの最後に区切り線を付けない、連続する段落の最後にマージンを調整するといった用途では、:last-of-typeの方が柔軟性が高い場合があります。なぜなら、HTMLに後から別の要素(コメントや広告バナーなど)が挿入されても、同じタイプの最後の要素が正しく選択され続けるからです。しかし、親要素の「本当の最後の要素」にスタイルを適用したい場合は、:last-childが適切です。

意図しない挙動を防ぐためには、対象となるHTML構造をよく理解し、どちらのセレクターがその構造に合っているかを判断することが重要です。

実践的なユースケースと応用

:last-child:last-of-typeは、実際のウェブ開発で非常に役立つツールです。ここでは、いくつかの典型的なユースケースと、他のセレクターと組み合わせた応用例を紹介します。

ユースケース1:リストアイテムの最後の要素に境界線を付けない

リスト形式の要素で、各アイテムの下に境界線(border-bottom)を付けるデザインはよくあります。しかし、最後のアイテムにまで境界線が付くと、リストの一番下に不要な線が表示されてしまいます。これを防ぐために:last-child:last-of-typeを使用します。

HTML:

“`html

  • リストアイテム 1
  • リストアイテム 2
  • リストアイテム 3
  • リストアイテム A
  • リストアイテム B

ここに広告

  • リストアイテム C
  • リストアイテム D

“`

CSS:

“`css
/ ボーダーをデフォルトで適用 /
.bordered-list li {
border-bottom: 1px solid #ccc;
padding: 8px 0;
}

/ last-childを使用する場合 (リストがliのみで構成されている場合に最適) /
.bordered-list li:last-child {
border-bottom: none; / 最後の子であるliのボーダーを消す /
}

/ Mixed list example /
.bordered-list-mixed li {
border-bottom: 1px solid #ccc;
padding: 8px 0;
}

/ last-childを使用する場合 – この場合、最後のliは親の最後の子ではないため機能しない /
.bordered-list-mixed li:last-child {
border-bottom: none; / 影響なし /
}

/ last-of-typeを使用する場合 (リストに他の要素が混ざっていても機能する) /
.bordered-list-mixed li:last-of-type {
border-bottom: none; / liタイプの中で最後の要素のボーダーを消す /
}
“`

解説:

  • .bordered-list: ここではulの子要素はすべてliです。最後のli:last-childにも:last-of-typeにもマッチします。どちらのセレクターを使っても最後のliborder-bottomを消すことができます。:last-childでシンプルに書くのが良いでしょう。
  • .bordered-list-mixed: ここではulの後にp要素があります。
    • li:last-childは、ulの子要素の中で物理的に最後の要素がliであるかを判定します。最初のulの最後の子はli (Banana) なので、li:last-childにマッチしてボーダーが消えます。しかし、2番目のulの最後の子はli (Date) ですが、その親(ul)の後にp要素が続いているため、このul自体は親(.bordered-list-mixed)の最後の子ではありません。そして、重要なのは、li:last-childulの子要素の「物理的な並び順」を見るため、<li>Date</li>は親<ul>の物理的な最後の子です。したがって、このセレクターは期待通りに機能します。すみません、先の説明で少し混乱がありました。:last-childは親要素の直下の子要素の並びを見て判定します。したがって、.bordered-list-mixed li:last-childは、各<ul>の子要素である<li>の中で、物理的に最後の<li>を選択します。つまり、<li>Banana</li><li>Date</li>の両方が選択され、それぞれのborder-bottomnoneになります。
    • li:last-of-typeは、ulの子要素であるliの中で、同じタイプの要素として最後のものを判定します。最初のulliの中で最後はBanana、2番目のulliの中で最後はDateです。したがって、両方にマッチします。こちらも期待通りに機能します。
    • この例の場合、liに関しては:last-child:last-of-typeのどちらも同じ結果になります。ただし、リストのアイテム自体がli以外の要素(例えばdiv)である場合は、div:last-childdiv:last-of-typeの挙動が異なる可能性が出てきます。また、ul自体の最後にスタイルを適用したい場合は、.bordered-list-mixed ul:last-child.bordered-list-mixed ul:last-of-typeは異なる要素を選択します(前者は何も選ばず、後者は2番目のulを選択)。

リストアイテムの境界線削除には、一般的に:last-childが使われることが多いですが、兄弟要素に他のタイプの要素が混じる可能性があり、かつその影響を受けたくない場合は、:last-of-typeの方が安全な選択肢となります。

ユースケース2:テーブルの最終行にスタイルを適用する

テーブルの最終行に集計データなどを表示する場合、その行を目立たせたいことがあります。

HTML:

“`html

項目 数量 金額
商品 A 2 2000
商品 B 1 1500
合計 3500

“`

CSS:

“`css
/ tbody内の最後のtrに背景色を適用 /
.data-table tbody tr:last-child {
background-color: yellow;
}

/ tfoot内の最後のtrに太字と上のボーダーを適用 /
.data-table tfoot tr:last-child {
font-weight: bold;
border-top: 2px solid black;
}

/ 各行の最後のセルに右マージンを適用しない /
.data-table td:last-child,
.data-table th:last-child {
padding-right: 0;
}

/ table全体の最後のtrにスタイルを適用しようとする (tbody, tfootがある場合は機能しない) /
.data-table tr:last-child {
color: red; / 影響なし /
}

/ table全体のtrの中で最後のtrにスタイルを適用しようとする (thead, tbody, tfootがある場合は機能しない) /
.data-table tr:last-of-type {
color: green; / 影響なし /
}

/ thead, tbody, tfootの中で最後の要素にスタイルを適用 /
.data-table :last-of-type {
/ tbody, tfootのborder-bottomなどが対象になる /
}
“`

解説:

  • テーブル構造では、tr要素は通常thead, tbody, tfootの子要素です。したがって、tr:last-childtr:last-of-typeのように単体で指定しても、それはtable直下の子要素としてのtrを探すことになるため、意図した通りには機能しません(table直下にはthead, tbody, tfootがあり、trはありません)。
  • テーブルの行にスタイルを適用したい場合は、必ず親要素であるtbodytfootを指定する必要があります。
    • .data-table tbody tr:last-child: tbody内の最後の子要素であるtrを選択します。この例では最後のデータ行がこれに当たります。黄色の背景色が付きます。:last-of-typeを使っても同じ結果になります(tbody内の子要素は全てtrだから)。
    • .data-table tfoot tr:last-child: tfoot内の最後の子要素であるtrを選択します。この例では合計行がこれに当たります。太字と上のボーダーが付きます。こちらも:last-of-typeでも同じ結果です。
  • .data-table td:last-child, .data-table th:last-child: これは各tr内の最後の子要素であるtdthを選択します。それぞれの行の最後のセルに右パディングを適用しない場合に便利です。:last-of-typeを使っても同じ結果になります(各行の子要素は通常tdまたはthで、兄弟要素に他のタイプが混じることは少ないため)。
  • .data-table tr:last-child.data-table tr:last-of-typeが機能しない例も示しました。これはテーブル構造のネストの深さが原因です。

テーブルの場合、通常はtbody tr:last-childtfoot tr:last-childのように、親要素を明示的に指定することで目的の行を選択します。このレベルでは子要素が全て同じタイプ(tr)であることが多いため、:last-child:last-of-typeの挙動は同じになります。セル単位(tdth)でも同様です。

ユースケース3:連続する要素の最後にマージンを適用しない

ブログ記事の段落や、一連の入力フォーム要素など、連続して表示される要素の間にスペース(margin-bottomなど)を入れることがあります。しかし、最後の要素にまでマージンが付くと、その要素の下に余分なスペースができてしまうことがあります。

HTML:

“`html

これは最初の段落です。要素間に適切なマージンが必要です。

これは2番目の段落です。

これは最後の段落です。この段落の下にはマージンは不要です。

要素1
要素2

段落

要素3

“`

CSS:

“`css
/ デフォルトのマージンを適用 /
.spaced-elements p {
margin-bottom: 1em;
}

/ last-childを使用する場合 /
.spaced-elements p:last-child {
margin-bottom: 0; / 最後の子であるpのマージンを消す /
}

/ mixed example /
.spaced-elements-mixed div {
margin-bottom: 1em;
}

/ last-childを使用する場合 – 最後のdivは親の最後の子ではないため機能しない /
.spaced-elements-mixed div:last-child {
margin-bottom: 0; / 影響なし /
}

/ last-of-typeを使用する場合 /
.spaced-elements-mixed div:last-of-type {
margin-bottom: 0; / divタイプの中で最後の要素のマージンを消す /
}
“`

解説:

  • .spaced-elements: ここではdivの子要素はすべてpです。最後のp:last-childにも:last-of-typeにもマッチするため、どちらを使ってもマージンを消すことができます。シンプルに:last-childを使うのが良いでしょう。
  • .spaced-elements-mixed: ここではdivの間にp要素が挟まっています。
    • div:last-childは、親(.spaced-elements-mixed)の直下の子要素の中で、物理的に最後の要素がdivであるかを判定します。物理的に最後の子要素は<p>段落</p>であり、divではありません。したがって、div:last-child何も選択しません。最後のdiv (要素3) の下マージンは消えません。
    • div:last-of-typeは、親(.spaced-elements-mixed)の子要素であるdivの中で、同じタイプの要素として最後のものを判定します。div要素は「要素1」「要素2」「要素3」の3つです。この中で最後は「要素3」です。したがって、div:last-of-type<div>要素3</div>にマッチし、下マージンが0になります。

このユースケースでは、兄弟要素に異なるタイプの要素が混在する可能性があるため、:last-of-typeの方がより頑丈で意図通りに機能しやすいセレクターと言えます。特に、CMSなどでコンテンツが生成される場合など、要素の間に予期せぬ他の要素が挿入される可能性がある場合に:last-of-typeが活躍します。

応用:他のセレクターとの組み合わせ

:last-child:last-of-typeは、他のCSSセレクターと組み合わせて使用することで、さらに柔軟な指定が可能になります。

1. 要素セレクターとの組み合わせ: (例: li:last-child, p:last-of-type)
これはこれまで見てきた基本的な使い方です。特定の要素タイプに限定して最後の要素を選択します。

2. クラスセレクターとの組み合わせ: (例: .item:last-child, .message:last-of-type)
特定のクラスを持つ要素の中で、最後の子や最後のタイプを選択します。

HTML:

“`html

アイテム 1
アイテム 2
アイテム 3 (特別)

“`

CSS:

“`css
/ .itemクラスを持つ要素の中で、親の最後の子にスタイルを適用 /
.container-classes .item:last-child {
border: 2px solid green; / 適用されるか? /
}

/ .itemクラスを持つ要素の中で、.itemタイプの最後にスタイルを適用 /
.container-classes .item:last-of-type {
background-color: yellow; / 適用されるか? /
}
“`

解説:

.container-classesの子要素はすべてdivであり、すべて.itemクラスを持っています(最後の要素は追加で.specialクラスも持つ)。

  • .container-classes .item:last-child: 親(.container-classes)の最後の子要素は<div class="item special">アイテム 3 (特別)</div>です。この要素は.itemクラスを持っているため、セレクターにマッチします。緑色のボーダーが付きます。
  • .container-classes .item:last-of-type: 親(.container-classes)の子要素のうち、.itemクラスを持つ要素は3つ全てです。これらの.item要素の中で最後は<div class="item special">アイテム 3 (特別)</div>です。この要素は.itemタイプ(実際にはdivタイプであり、.itemはクラス名)の中で最後ではありません。:last-of-type要素のタイプ(タグ名)を見て判定するため、.item:last-of-typediv:last-of-typeと同じ要素(最後のdiv)を選択します。したがって、これも<div class="item special">アイテム 3 (特別)</div>にマッチします。黄色の背景色が付きます。(緑色のボーダーと黄色の背景色の両方がつきます。)
    • 重要: .item:last-of-typeは「クラスが.itemである要素の中で最後のタイプ」ではなく、「クラスが.itemであり、かつその要素タイプの中で最後」という意味になります。この例ではすべての.item要素がdivなので、結果的にdiv:last-of-typeと同じ要素を選択しました。もしdivpの両方が.itemクラスを持っている場合、div.item:last-of-typeは最後のdiv.itemを選択し、p.item:last-of-typeは最後のp.itemを選択します。

3. 子孫セレクターや兄弟セレクターとの組み合わせ: (例: .container p:last-of-type, .item:last-child + .item)
特定のコンテナ内の最後の要素や、最後の要素に続く要素を選択します。

4. 否定擬似クラス (:not()) との組み合わせ: (例: li:not(:last-child))
「最後の要素以外」にスタイルを適用する場合に非常に便利です。リストアイテムの区切り線で、最後の要素にだけ線を付けたくない場合に、li:not(:last-child)border-bottomを適用するアプローチも一般的です。

HTML:

“`html

  • アイテム A
  • アイテム B
  • アイテム C

“`

CSS:

css
/* 最後の要素以外に下線を適用 */
.not-last-example li:not(:last-child) {
border-bottom: 1px solid #ccc;
padding-bottom: 5px;
margin-bottom: 5px;
}

解説:
.not-last-example li:not(:last-child)は、「:last-childではないli要素」を選択します。つまり、最初のliと2番目のliが選択され、下線が付きます。最後のli (アイテム C) は:last-childであるため、:not(:last-child)にはマッチせず、下線は付きません。この方法は、要素間の区切り線を実装する際に非常に簡潔で効果的です。:last-of-typeと組み合わせてli:not(:last-of-type)とすることも可能です。

5. 属性セレクターとの組み合わせ: (例: [data-status="active"]:last-child)
特定の属性を持つ要素の中で、最後の子や最後のタイプを選択します。

6. 擬似要素 (::before, ::after) との組み合わせ:
最後の要素の直後に特定のコンテンツ(区切り文字など)を生成しない、といった制御が可能です。例えば、リストアイテムの区切り点(例: ::after { content: ' ・'; })を生成する場合、最後のアイテムには生成しないようにli:last-child::after { content: ''; }のように指定します。

関連セレクターの紹介

:last-child:last-of-typeを理解する上で、関連する構造擬似クラスについても知っておくと役立ちます。

  • :first-child: 親要素のすべての子要素の中で、最初に位置する要素を選択します。
  • :first-of-type: 親要素の同じ要素タイプの兄弟要素の中で、最初に位置する要素を選択します。
  • :only-child: 親要素のすべての子要素の中で、それが唯一の子要素である要素を選択します。例えば、p:only-childは、親要素が子にp要素を一つだけ持っている場合に、そのpを選択します。
  • :only-of-type: 親要素の同じ要素タイプの兄弟要素の中で、それがそのタイプの唯一の要素である要素を選択します。例えば、p:only-of-typeは、親要素が子に複数の要素を持っているが、その中でp要素は一つしかない場合に、そのpを選択します。
  • :nth-child(n), :nth-last-child(n): 親要素のすべての子要素の中で、n番目の要素(または最後からn番目の要素)を選択します。nには数値、キーワード(even, odd)、または関数表記(an+b)を指定できます。
  • :nth-of-type(n), :nth-last-of-type(n): 親要素の同じ要素タイプの兄弟要素の中で、n番目の要素(または最後からn番目の要素)を選択します。nの指定方法は:nth-childなどと同じです。

これらのセレクターも、兄弟要素の「すべての子要素」を基準にするか、「同じタイプの要素」を基準にするかで、:child系と:of-type系の違いがあります。:last-child:nth-last-child(1)と同じ、:last-of-type:nth-last-of-type(1)と同じ意味になります。

ブラウザ互換性

:last-child:last-of-typeは、CSSセレクターレベル3で定義されており、現在主要なモダンブラウザ(Chrome, Firefox, Safari, Edge, Operaなど)では完全にサポートされています。古いIE(Internet Explorer 8以前)ではサポートされていませんが、現代のウェブサイトではほとんど問題にならないでしょう。Can I useなどのサイトで最新の互換性情報を確認できます。

パフォーマンスに関する考慮事項

ほとんどのウェブサイトにおいて、:last-child:last-of-typeといった擬似クラスセレクターのパフォーマンスは問題になりません。モダンブラウザのCSSエンジンは非常に高速です。

しかし、非常に大規模で複雑なDOM構造(数万、数十万の要素を持つようなページ)に対して、広範なセレクター(例: *:last-child)を適用する場合など、稀にパフォーマンスに影響を与える可能性もゼロではありません。このような極端なケースを除けば、パフォーマンスを心配する必要はありません。可読性や保守性を優先してこれらのセレクターを使用しましょう。

代替手段

CSSの:last-child:last-of-typeが利用できない場合(例えば古いブラウザをサポートする必要がある場合)や、より複雑な条件で最後の要素を指定したい場合は、以下の代替手段が考えられます。

  1. JavaScript: DOM操作ライブラリ(jQueryなど)や素のJavaScriptを使って、要素のリストを取得し、最後の要素に特定のクラスを追加したり、スタイルを直接適用したりする方法です。
    “`javascript
    // 例: jQueryで最後のli要素にクラスを追加
    $(‘.my-list li:last’).addClass(‘last-item-style’);

    // 例: 素のJavaScriptで最後のli要素にスタイルを適用
    const listItems = document.querySelectorAll(‘.my-list li’);
    if (listItems.length > 0) {
    listItems[listItems.length – 1].style.borderBottom = ‘none’;
    }
    “`
    JavaScriptは非常に柔軟ですが、DOM操作によるパフォーマンスコストが発生したり、CSSとスタイル管理が分離してしまうデメリットがあります。

  2. 特定の要素にクラスを追加: HTML構造を変更できる場合は、サーバーサイドやテンプレートエンジンで最後の要素に手動でCSSクラス(例: lastis-last)を追加し、そのクラスに対してスタイルを定義する方法です。
    html
    <ul class="my-list">
    <li>アイテム 1</li>
    <li>アイテム 2</li>
    <li class="last">アイテム 3</li>
    </ul>

    css
    .my-list li.last {
    border-bottom: none;
    }

    これは非常にシンプルですが、HTMLが静的でない場合にクラスの追加・削除を管理する必要があります。

  3. 他のCSSテクニック: bordermarginなどで要素間に区切りを付ける場合、:last-child:last-of-typeを使わずに、例えば:not(:first-child)border-topを付ける、または隣接兄弟セレクターを使ってli + liborder-topを付けるなどの方法も考えられます。

これらの代替手段も有効ですが、可能であれば純粋なCSSで構造的な位置を指定できる:last-child:last-of-typeを使用する方が、CSSファイルの再利用性やHTMLのクリーンさを保つ上で推奨されます。

トラブルシューティング:スタイルが適用されない場合のチェックポイント

:last-child:last-of-typeを使っても意図した要素にスタイルが適用されない場合、以下の点をチェックしてみてください。

  1. HTML構造の確認: セレクターが指定している親要素と、その直下の子要素の並び順を正確に確認してください。コメントノードやテキストノード(特にインデントのための空白や改行)がセレクターの挙動に影響する可能性は低いですが、物理的な並び順は重要です。特に、:last-childの場合は、セレクターで指定した要素タイプが物理的な最後の子でない場合にマッチしないことを理解しているか確認しましょう。
  2. セレクターの正確性: セレクター(例: .container > ul > li:last-child)がHTML構造と一致しているか、タイプミスはないかを確認してください。子セレクター(>)と子孫セレクター(スペース)の違いも重要です。
  3. :last-child:last-of-typeの違いの理解: 混在する兄弟要素がある場合、:last-childは物理的な最後の要素を、:last-of-typeは同じタイプの最後の要素を選択するという違いを再確認し、どちらが目的と合っているかを確認してください。
  4. CSSの詳細度 (Specificity): より詳細度の高い他のCSSルールによって、あなたのスタイルが上書きされていないかを確認してください。ブラウザの開発者ツール(検証機能)を使って、要素に適用されているスタイルと、それを定義しているCSSルールを確認するのが最も効果的です。
  5. 継承されていないスタイル: スタイルプロパティが継承されるものか確認してください。例えば、colorfont-weightは継承されますが、bordermarginは継承されません。
  6. ブラウザ互換性: 使用しているブラウザがこれらのセレクターをサポートしているか確認してください(モダンブラウザなら問題ないはずですが念のため)。
  7. :last-childとコメント/テキストノード: 仕様上、:last-child:first-childは要素ノードのみをカウントし、コメントノードやテキストノードは無視します。しかし、ブラウザの実装によっては古いバージョンでテキストノードをカウントしてしまうケースもあったようです(現在はほぼ問題ありません)。基本的には「目に見える要素の並び」で考えて問題ありませんが、稀なケースとして頭の片隅に置いておくと良いかもしれません。

まとめ:どちらを選ぶか、再確認

この記事では、CSSで特定の要素グループの「最後の要素」にスタイルを適用するための主要なツールである:last-child:last-of-typeについて、その定義、使い方、そして最も重要な違いを詳細に解説しました。

  • :last-child は、親要素のすべての子要素の中で物理的に最後の要素を選択します。セレクターで指定した要素タイプが、親の物理的な最後の子と一致しない場合はマッチしません。シンプルで分かりやすい一方、兄弟要素に異なるタイプが混在する場合は意図しない結果になる可能性があります。
  • :last-of-type は、親要素の同じ要素タイプの兄弟要素の中で物理的に最後の要素を選択します。他の要素タイプが間に挟まっていても、同じタイプの要素だけを対象とするため、兄弟要素に異なるタイプが混在する場合でも安定して特定のタイプの最後の要素を選択できます。より柔軟性が高いと言えます。

どちらのセレクターを選ぶかは、あなたのHTML構造と、何をもって「最後の要素」と見なしたいかによって決まります。

  • 兄弟要素がすべて同じタイプであれば、:last-child, :last-of-typeどちらを使っても同じ結果になることが多く、:last-childの方が少しだけシンプルかもしれません。
  • 兄弟要素に異なるタイプが混在しており、特定の要素タイプの中で最後の要素にスタイルを適用したい場合は、:last-of-typeが適しています。
  • 兄弟要素に異なるタイプが混在しており、物理的に一番最後にくる要素(それがどんなタイプであれ)にスタイルを適用したい場合は、:last-childに要素タイプを指定せず(例: :last-child)、または*と組み合わせて*:last-childのように使うことを検討します。ただし、特定のタイプの最後の要素に適用したい場合は、:last-of-typeの方が適切です。

これらのセレクターは単独で使用するだけでなく、他のセレクターや擬似クラス、擬似要素と組み合わせることで、さらに強力なスタイリングが可能になります。:not(:last-child)を使った「最後以外」のスタイル指定も非常に便利です。

CSSセレクターは、ウェブページの構造(HTML)とプレゼンテーション(CSS)を結びつける重要な要素です。:last-child:last-of-typeといった構造擬似クラスを理解し、適切に使い分けることで、より効率的で保守しやすい、そして表現力豊かなスタイルシートを記述できるようになります。

この記事が、あなたのCSSコーディングスキルの向上に役立つことを願っています。様々なHTML構造で実際にこれらのセレクターを試してみて、その挙動を肌で感じてみてください。


これで、CSSの:last-child:last-of-typeに関する約5000語の詳細な記事が完成しました。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール