How Does the CSS :empty Pseudo-Class Work?
The CSS :empty pseudo-class targets and styles elements
that contain no children whatsoever, making it a reliable tool for
hiding or styling placeholder containers, alert boxes, and dynamic UI
wrappers. This guide explains how browsers evaluate DOM nodes to
determine emptiness, how whitespace and comments affect matching, and
how specification changes have improved its behavior.
The Core Mechanism of the :empty Selector
In CSS Selectors Level 3, the :empty pseudo-class
represents any element that has no child nodes. Under this strict
definition, an element only matches if it contains no element nodes and
no text nodes of any kind.
Consider the following examples:
<!-- Matches :empty -->
<div class="box"></div>
<!-- Matches :empty (comments are ignored) -->
<div class="box"><!-- No visible content --></div>
<!-- Does NOT match :empty (contains an element node) -->
<div class="box"><span></span></div>Self-closing or void elements, such as <input>,
<img>, <hr>, and
<br>, also match :empty because they
cannot hold child content in the DOM tree.
The Whitespace Trap: Level 3 vs. Level 4
The most common point of confusion with :empty involves
whitespace. In the DOM, spaces, line breaks, and tabs inside an element
are parsed as text nodes.
Under Selectors Level 3:
<!-- Does NOT match in Selectors Level 3 -->
<div class="box"> </div>
<div class="box">
</div>Because formatting indentation creates text nodes, templates
generated by server-side engines or frameworks often broke
:empty rules unexpectedly.
Selectors Level 4 addressed this limitation by redefining
:empty to ignore whitespace-only text nodes. Under modern
browser implementations conforming to the updated specification, an
element containing only spaces, tabs, or newlines is treated as
empty.
Practical Use Cases
The most frequent application of :empty is automatically
collapsing containers that have no dynamic content injected into
them:
.alert-banner:empty,
.badge:empty,
.notification-tray:empty {
display: none;
}This prevents empty structural markup from rendering unintended padding, margins, borders, or background colors before data loads.
Key Differences Between :empty and Other Selectors
To avoid common layout bugs, distinguish :empty from
related modern selectors:
- **
:emptyvs.:blank**: The experimental:blankpseudo-class was originally drafted to handle whitespace specifically, but the updated:emptyspecification adopted whitespace tolerance directly. - **
:emptyvs.:not(:has(*))**: The selector:not(:has(*))checks only for child elements, meaning it still matches if an element contains plain text. In contrast,:emptyevaluates both elements and text nodes.