現場のエンジニアが陥る「:first-child/:last-child」の罠と、その「正解」
やあ。コードレビューをしていて、未だに「あ、ここは普通にclassを振ればいいのに…」とか「あ、その書き方だと将来痛い目を見るよ」とつぶやいてしまう箇所がある。
その筆頭が、今回扱う `:first-child` と `:last-child` だ。
教科書通りの「最初と最後を選ぶ」という機能は誰もが知っている。だが、「CSSの構造疑似クラスは、DOMの変動に驚くほど脆い」という現場の現実を、どれだけ深く理解しているだろうか。
今日は、中級エンジニアの君にこそ知っておいてほしい、この疑似クラスの「裏の顔」と、明日から使えるスマートな実装術を授けよう。
—
1. ブラウザは「最初の子」をどう認識しているのか?
まず、ブラウザのレンダリングエンジンがどう動いているかを想像してみてほしい。
`:first-child` は、「親要素の最初の子要素」を探す。ここで重要なのは、「最初の子要素が、指定したセレクタと一致しているか」をチェックしている点だ。
例えば、以下のような構造があるとする。
タイトル
- 1
- 2
ここで `.list-wrapper li:first-child` と書いても、何も起きない。なぜなら、親(.list-wrapper)の最初の子は `p` タグであり、`li` ではないからだ。
「なんで効かないんだ?」とデベロッパーツールで首を傾げる時間は、エンジニア人生で最も無駄な時間の一つだ。「親の直下にある全要素の中で、最初の子が自分(セレクタ)である場合にのみ適用される」。この言葉を脳に刻んでおいてくれ。
—
2. 実務で「これだけは知っておけ」という実装パターン
リストのボーダー処理などは、この疑似クラスの独壇場だ。ベタ打ちで `border-bottom` をつけると、最後の要素の下にも線がついてしまい、デザイン崩れを起こす。
現場で一番きれいな、いわゆる「モダン・リセット」の書き方がこれだ。
.card-list {
display: flex;
flex-direction: column;
}
.card-item {
/ 全ての要素に上のボーダーを引く /
border-top: 1px solid #ccc;
}
/ 最初の要素にはボーダーを表示しない /
.card-item:first-child {
border-top: none;
}
/ 最後の要素だけ、下のボーダーも引く(リストの閉じとして) /
.card-item:last-child {
border-bottom: 1px solid #ccc;
}
この書き方の何が優れているか? それは、「要素が後から増減しても、CSS側でケアする必要がない」というメンテナンス性の高さだ。クラス名を汚さずに、構造だけでUIを担保する。これぞ、CSSアーキテクトの矜持というものだ。
—
3. 「:last-child」の死角と、現代の解決策
現場でよくある悲劇がこれだ。
CMSで動的に要素が追加される際、最後に「隠し要素(scriptタグや、エラー用の空divなど)」が紛れ込んでしまうケース。
`div:last-child` と書いているのに、期待通りに動かない。なぜなら、HTML上では空のタグも立派な「子供」としてカウントされるからだ。
もし君が「中身のある要素の最後だけ」をターゲットにしたいなら、`:last-child` ではなく `:last-of-type` を検討する冷静さが必要だ。
- :last-child: 親の最後の子が「その要素」であれば適用
- :last-of-type: 親の子供の中で「その要素型」の最後のものに適用
「どっちを使うべきか」迷ったら、そのリストが「DOM構造として純粋か、ノイズが混じる可能性があるか」を考える。後者なら、迷わず `:last-of-type` を選ぶのがプロの判断だ。
—
4. 最後に:CSSは「守り」の設計こそが美しい
CSSをいかに書くかよりも、「いかに書かないか」。これが中級からシニアへ上がるための鍵だ。
一つ一つに `is-first` とか `is-last` といったクラスをJSやPHPで付与するコードをたまに見かけるが、それはCSSの機能を捨てているのと同義だ。ブラウザのエンジンに仕事をさせよう。彼らは、人間が書いたコードよりもはるかに速く、正確にDOMを計算してくれる。
今日持ち帰ってほしい教訓はたった一つ。
「構造疑似クラスは便利だが、親要素のDOM構造と運命共同体である」ということ。
もしUIが壊れたら、まず疑うべきはCSSの記述ミスではなく、親要素の中に予期せぬDOMが入り込んでいないかを確認する。その視点を持てたとき、君のフロントエンドエンジニアとしてのレベルは一段階上の景色が見えているはずだ。
さて、コードを書いていこうか。君が書くその一行が、明日誰かのメンテナンスを楽にすることを願っているよ。

コメント