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
というセレクターがあった場合、これは「親要素(例えばul
やol
)の直下にあるすべての子要素の中で最後に位置する要素であり、かつその要素がli
であるもの」を選択します。もし親要素の最後の子がli
以外の要素(例えばp
やdiv
)だった場合、その親の最後の子はli
ではないため、li:last-child
は何も選択しません。
基本的な使い方(単一の親要素内の子要素)
最も一般的な使い方は、リスト (ul
や ol
) の中の最後のリストアイテム (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; / 最後の子要素に背景色 /
}
“`
解説:
.list-last-child li:last-child
: 最初の例では、ul
の子要素はすべてli
です。最後のli
は「リストアイテム 3」です。このli
は親要素(ul
)の直下にあるすべての子要素の中で物理的に最後に位置する要素であり、かつli
であるため、:last-child
にマッチします。したがって、このセレクターは「リストアイテム 3」を選択し、青色で太字になります。border-bottom: none;
も適用されます。.list-last-child-mixed li:last-child
: 2番目の例では、ul
の子要素はli
、li
、p
、li
の順に並んでいます。物理的に最後の子要素は<li>アイテム C</li>
ではありません。最後の子要素は<p>これはリスト外の段落</p>
です。li:last-child
セレクターは、「親の子要素の中で最後に位置する要素であり、かつその要素がli
であるもの」を探します。しかし、最後の子要素はp
であるため、li
ではありません。したがって、このセレクターは何も選択しません。<li>アイテム C</li>
には赤色が適用されません。.list-last-child-mixed :last-child
: このセレクターは、.list-last-child-mixed
の子孫要素の中で、それぞれが属する親要素の最後の子であるもの全てを選択します。この場合、ul
の子要素の中で最後の子である<p>これはリスト外の段落</p>
が選択され、背景色が黄色になります。また、li
やp
の中にさらに子要素があれば、その最後の子も選択されますが、この例では子要素を持たないため、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」を含む行です。このtr
はtbody
の最後の子であり、かつ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
というセレクターがあった場合、これは「親要素(例えばul
やol
)の直下にある子要素のうち、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;
}
“`
解説:
.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」は緑色で太字になります。.list-last-of-type p:last-of-type
:.list-last-of-type
の子要素のうち、p
要素だけを抜き出すと<p>これはリスト外の段落</p>
の1つだけです。このp
要素は、p
要素のグループの中では当然最後に位置します。したがって、p:last-of-type
にマッチし、イタリック体で下線が付きます。.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
タイプの中で最後の子は最後のp
。p: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
)の兄弟要素の中で最後であるかを見ます。最初のul
のli
要素は「Apple」「Banana」です。この中で最後のli
は「Banana」です。2番目のul
のli
要素は「Cherry」「Date」です。この中で最後のli
は「Date」です。したがって、両方の「Banana」と「Date」がイタリック体になります。(太字とイタリック体の両方がつくことになります)
この比較から、:last-child
と:last-of-type
は、特に兄弟要素に異なるタイプの要素が混在する場合に、マッチする要素が異なることが分かります。どちらのセレクターを使うかは、あなたが「物理的な最後の子要素」にスタイルを適用したいのか、それとも「特定の要素タイプの中で最後の要素」にスタイルを適用したいのかによって決まります。
どちらを選ぶべきか?判断の基準
:last-child
を選ぶべきケース:- 親要素内の子要素がすべて同じタイプであることが保証されている場合 (例: 純粋な
ul > li
、ol > li
、select > option
など)。 - 親要素内の子要素に異なるタイプが混在していても、物理的に最後の要素にスタイルを適用したい場合。例えば、特定のコンテナ内の最後の要素に「bottom-padding」を適用したいが、その要素が
p
であろうとdiv
であろうと構わない場合など。
- 親要素内の子要素がすべて同じタイプであることが保証されている場合 (例: 純粋な
:last-of-type
を選ぶべきケース:- 親要素内の子要素に異なるタイプが混在しており、特定の要素タイプの中から最後の要素にスタイルを適用したい場合。例えば、
div
の中に複数のh2
とp
があり、最後の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
にもマッチします。どちらのセレクターを使っても最後のli
のborder-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-child
はul
の子要素の「物理的な並び順」を見るため、<li>Date</li>
は親<ul>
の物理的な最後の子です。したがって、このセレクターは期待通りに機能します。すみません、先の説明で少し混乱がありました。:last-child
は親要素の直下の子要素の並びを見て判定します。したがって、.bordered-list-mixed li:last-child
は、各<ul>
の子要素である<li>
の中で、物理的に最後の<li>
を選択します。つまり、<li>Banana</li>
と<li>Date</li>
の両方が選択され、それぞれのborder-bottom
がnone
になります。li:last-of-type
は、ul
の子要素であるli
の中で、同じタイプの要素として最後のものを判定します。最初のul
のli
の中で最後はBanana
、2番目のul
のli
の中で最後はDate
です。したがって、両方にマッチします。こちらも期待通りに機能します。- この例の場合、
li
に関しては:last-child
と:last-of-type
のどちらも同じ結果になります。ただし、リストのアイテム自体がli
以外の要素(例えばdiv
)である場合は、div:last-child
とdiv: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-child
やtr:last-of-type
のように単体で指定しても、それはtable
直下の子要素としてのtr
を探すことになるため、意図した通りには機能しません(table
直下にはthead
,tbody
,tfoot
があり、tr
はありません)。 - テーブルの行にスタイルを適用したい場合は、必ず親要素である
tbody
やtfoot
を指定する必要があります。.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
内の最後の子要素であるtd
やth
を選択します。それぞれの行の最後のセルに右パディングを適用しない場合に便利です。:last-of-type
を使っても同じ結果になります(各行の子要素は通常td
またはth
で、兄弟要素に他のタイプが混じることは少ないため)。.data-table tr:last-child
や.data-table tr:last-of-type
が機能しない例も示しました。これはテーブル構造のネストの深さが原因です。
テーブルの場合、通常はtbody tr:last-child
やtfoot tr:last-child
のように、親要素を明示的に指定することで目的の行を選択します。このレベルでは子要素が全て同じタイプ(tr
)であることが多いため、:last-child
と:last-of-type
の挙動は同じになります。セル単位(td
やth
)でも同様です。
ユースケース3:連続する要素の最後にマージンを適用しない
ブログ記事の段落や、一連の入力フォーム要素など、連続して表示される要素の間にスペース(margin-bottom
など)を入れることがあります。しかし、最後の要素にまでマージンが付くと、その要素の下に余分なスペースができてしまうことがあります。
HTML:
“`html
これは最初の段落です。要素間に適切なマージンが必要です。
これは2番目の段落です。
これは最後の段落です。この段落の下にはマージンは不要です。
段落
“`
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
“`
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-type
はdiv:last-of-type
と同じ要素(最後のdiv
)を選択します。したがって、これも<div class="item special">アイテム 3 (特別)</div>
にマッチします。黄色の背景色が付きます。(緑色のボーダーと黄色の背景色の両方がつきます。)- 重要:
.item:last-of-type
は「クラスが.item
である要素の中で最後のタイプ」ではなく、「クラスが.item
であり、かつその要素タイプの中で最後」という意味になります。この例ではすべての.item
要素がdiv
なので、結果的にdiv:last-of-type
と同じ要素を選択しました。もしdiv
とp
の両方が.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
が利用できない場合(例えば古いブラウザをサポートする必要がある場合)や、より複雑な条件で最後の要素を指定したい場合は、以下の代替手段が考えられます。
-
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とスタイル管理が分離してしまうデメリットがあります。 -
特定の要素にクラスを追加: HTML構造を変更できる場合は、サーバーサイドやテンプレートエンジンで最後の要素に手動でCSSクラス(例:
last
やis-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が静的でない場合にクラスの追加・削除を管理する必要があります。 -
他のCSSテクニック:
border
やmargin
などで要素間に区切りを付ける場合、:last-child
や:last-of-type
を使わずに、例えば:not(:first-child)
にborder-top
を付ける、または隣接兄弟セレクターを使ってli + li
にborder-top
を付けるなどの方法も考えられます。
これらの代替手段も有効ですが、可能であれば純粋なCSSで構造的な位置を指定できる:last-child
や:last-of-type
を使用する方が、CSSファイルの再利用性やHTMLのクリーンさを保つ上で推奨されます。
トラブルシューティング:スタイルが適用されない場合のチェックポイント
:last-child
や:last-of-type
を使っても意図した要素にスタイルが適用されない場合、以下の点をチェックしてみてください。
- HTML構造の確認: セレクターが指定している親要素と、その直下の子要素の並び順を正確に確認してください。コメントノードやテキストノード(特にインデントのための空白や改行)がセレクターの挙動に影響する可能性は低いですが、物理的な並び順は重要です。特に、
:last-child
の場合は、セレクターで指定した要素タイプが物理的な最後の子でない場合にマッチしないことを理解しているか確認しましょう。 - セレクターの正確性: セレクター(例:
.container > ul > li:last-child
)がHTML構造と一致しているか、タイプミスはないかを確認してください。子セレクター(>
)と子孫セレクター(スペース)の違いも重要です。 :last-child
と:last-of-type
の違いの理解: 混在する兄弟要素がある場合、:last-child
は物理的な最後の要素を、:last-of-type
は同じタイプの最後の要素を選択するという違いを再確認し、どちらが目的と合っているかを確認してください。- CSSの詳細度 (Specificity): より詳細度の高い他のCSSルールによって、あなたのスタイルが上書きされていないかを確認してください。ブラウザの開発者ツール(検証機能)を使って、要素に適用されているスタイルと、それを定義しているCSSルールを確認するのが最も効果的です。
- 継承されていないスタイル: スタイルプロパティが継承されるものか確認してください。例えば、
color
やfont-weight
は継承されますが、border
やmargin
は継承されません。 - ブラウザ互換性: 使用しているブラウザがこれらのセレクターをサポートしているか確認してください(モダンブラウザなら問題ないはずですが念のため)。
: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語の詳細な記事が完成しました。