お疲れ様です!チーフフロントエンドアーキテクトの「T」です。
日々のコーディング、本当にお疲れ様。モダンなコンポーネント設計やJavaScript/TypeScriptのロジックに頭を悩ませることが多いと思いますが、実はUIの使い勝手や表示の美しさを最後の最後で担保するのは、いつの時代も「CSSのきめ細やかなコントロール」です。
今日、皆さんに共有したいのは、地味ながらも現場でバグの温床になりがちで、同時にマスターすると劇的にコードをスッキリさせられる魔法の疑似クラス、`:empty`についてです。
「要素の中身が空っぽのときに非表示にしたい」
「CMSから出力される空の `
` タグが、マージンだけ残ってデザインを崩してしまう」
こんな局面、一度は遭遇したことがありますよね。JSで要素の有無を監視してクラスを切り替える……なんて大掛かりなことをしなくても、CSSだけでエレガントに解決できる武器が`:empty`です。しかし、こいつには「仕様上の罠」が潜んでいます。
今日は、ブラウザが裏側でどう処理しているかという深層から、現場ですぐに使える実践的なテクニックまで、徹底的に解説します。準備はいいですか?
—
1. そもそも `:empty` とは何者か? その厳格な仕様を知る
まずは基本に立ち返りましょう。W3Cの仕様において、`:empty` 疑似クラスが「空(Empty)」と判定する条件は非常に厳格です。
> 「子要素(要素ノード)も、テキスト(テキストノード)も一切持たない要素」
これだけです。非常にシンプルに見えますよね。しかし、実務で「効かない!」と頭を抱える原因の9割は、この「テキストノード」の定義にあります。
ブラウザの裏側で何が起きているか?
ブラウザがHTMLを解析してDOM(Document Object Model)ツリーを構築するとき、タグとタグの間に存在する「改行」や「半角スペース」、「タブ」を、すべて「テキストノード(Text Node)」として生成します。
例えば、以下の3つのコードを見てください。
直感的には、これら3つはすべて「空のバッジ」に見えますよね。しかし、CSSの `:empty` がマッチするのは「パターンA」だけです。
- パターンA: 子ノードが0個 ➔ `:empty` にマッチする
- パターンB: 改行+インデント(スペース数個)という「テキストノード」が存在する ➔ `:empty` にマッチしない
- パターンC: 半角スペース1文字という「テキストノード」が存在する ➔ `:empty` にマッチしない
ブラウザにとって、改行やスペースは「表示すべき文字(ホワイトスペース)」と同等です。そのため、ソースコードの見た目を綺麗に整えようとしてインデントを入れた瞬間、`:empty` は沈黙します。これが、多くの開発者が最初に踏む地雷です。
—
2. 現場を救う、実用的なCSS実装パターン
仕様がわかったところで、実際の開発で使えるパターンを見ていきましょう。
そのままプロジェクトにコピペして使える、実用的なコード例を用意しました。
ユースケース1: 動的な通知バッジ(要素が空なら消す)
まずは最もよくある「通知バッジ(Notification Badge)」の例です。
新着メッセージがあるときは数字を表示し、0件のときは要素自体(赤丸など)を非表示にしたい、というケースですね。
/ ベースのバッジスタイル /
.badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 20px;
height: 20px;
padding: 0 6px;
border-radius: 10px;
background-color: #ff3b30;
color: #ffffff;
font-size: 11px;
font-weight: bold;
/
アニメーションやトランジションを仕込んでおくと、
値が消えたときに滑らかに消すことも可能です。
/
transition: all 0.2s ease-in-out;
}
/
【秘伝】要素が完全に空(:empty)の時の処理。
display: none; にすることで、余計な領域や背景、ボーダーが一切消滅します。
/
.badge:empty {
display: none;
}
ユースケース2: CMSの空のラッパーを自動的に掃除する
WordPressやヘッドレスCMSなどで記事をレンダリングする際、エディタの改行癖のせいで「空の `
` タグ」や、動的パーツが外れて「空になった `
これをCSS側で自動的に「お掃除」してしまいましょう。
/
記事本文内の空の段落タグや、動的に空になったコンテナを非表示にする。
これによって、意図しないダブルマージンを防ぎます。
/
.article-body p:empty,
.dynamic-widget-area:empty {
display: none;
}
—
3. 「空白文字」の罠を回避するプロのテクニック
「そうは言っても、チーム開発でコーダー全員に『空要素の中に絶対に改行やスペースを入れるな』と強制するのは難しい……」
その通りです。コードフォーマッター(Prettierなど)の設定によっては、自動的に空要素の中に改行を挿入してしまうケースすらあります。
この現実的な問題に対処するために、シニアエンジニアとして知っておくべき回避策がいくつかあります。
対策A: コメントノードを活用する
実は、`:empty` の仕様上、「HTMLのコメント」は無視されます。
どうしてもインデントや改行を入れたい場合は、以下のようにコメントで埋めることで、ブラウザに「テキストノードではない」と認識させつつ、コードの視認性を保つことができます。
ちょっとトリッキーですが、テンプレートエンジンの都合でどうしても改行が入ってしまう場合の最後の手段として有効です。
対策B: JavaScriptでの「値のクリア」に細心の注意を払う
JavaScriptで動的に要素の中身を空にする際、その書き方によって `:empty` が効くかどうかが変わります。
const badge = document.querySelector(‘.badge’);
// ❌ 悪い例: 半角スペースを入れてしまう
badge.textContent = ‘ ‘; // これだと :empty は効かない!
// ❌ 悪い例: 改行を入れてしまう
badge.innerHTML = `
`; // これも :empty は効かない!
// 良い例: 完全に空の文字列を代入する
badge.textContent = ”; // これでバッチリ :empty が効く!
// 良い例: 既存の子ノードをすべて削除する
badge.replaceChildren(); // モダンブラウザならこれで一発で完全空化
JSの実装担当メンバーにも、この「空文字の扱い」をレビューでしっかり伝えて共有しておきましょう。フロントエンドの連携プレーが光るポイントです。
対策C: CSS Grid/Flexbox のギャップ(gap)の仕様を逆手に取る
もし `:empty` の挙動に依存してレイアウトを制御するのが不安な場合、レイアウト手法自体をモダンにアップデートするのも手です。
昔は、要素間に隙間を作るために `.card + .card { margin-top: 16px; }` のような隣接セレクタを使っていましたが、これだと空の要素が挟まった時にマージンが崩れます。
現在のモダンブラウザであれば、`gap` プロパティを使うことで、「実態のある(目に見える)要素の間」だけに自動で余白を適用してくれます。
.card-list {
display: flex;
flex-direction: column;
gap: 16px; / 中身が空で高さ0の要素があっても、レイアウト崩れが起きにくい /
}
—
4. まとめ:`:empty` はシンプルだからこそ、基礎力が試される
CSSの `:empty` は、正しく仕様を理解していれば、JSのコード量を減らし、宣言的(Declarative)でクリーンなスタイルシートを書くための強力な味方になります。
- `:empty` は「子要素もテキストも(スペース1つすら)存在しない」ときだけ反応する
- 改行やスペースは、ブラウザによって「テキストノード」として扱われるためNG
- JSでクリアするときは `textContent = ”` を徹底する
- テンプレートエンジンの自動インデントやPrettierの挙動に注意する
地味なプロパティですが、こういったディテールにどれだけこだわれるかで、プロダクトの堅牢性とコードの美しさが決まります。チーム内で「なんかCSSが効かない!」と悩んでいるメンバーがいたら、ぜひドヤ顔で(優しく)教えてあげてください。
これからも一歩先のフロントエンドを目指して、一緒に突き詰めていきましょう!

コメント