Hiding empty elements with CSS :empty and :has()
You might be used to adding and removing .open
and .closed
classes on div
s and containers to control their state styles. What if we could just write CSS that reflected the state of the DOM, without having to additionally handle state by toggling classes? In this post, we’ll explore how to hide elements with the :empty
pseudo-class if they have no children, and how to make the pattern more granular and practical when we combine it with :has()
and :not()
.
Hiding an empty element
The :empty
matches any element that has no children. The pseudo-class is supported by all major browsers, and is safe to use even if you’re targeting Internet Explorer. We can use it in combination with the display
property to hide an element if it’s empty:
.container:empty {
display: none;
}
In this example, the :empty
pseudo-class is used to select elements with the class .container
that have no children (including text nodes), and the display: none
rule is used to hide them.
<!-- This will be visible -->
<div class="container">Some text</div>
<!-- This will be hidden -->
<div class="container"></div>
Hiding an element that has an empty child
Assume that we have a some HTML markup that looks something like this, that we dynamically populate with suggestions inside of .results
…
<div class="container">
<h4>Suggestions</h4>
<div class="results">
...
</div>
</div>
…and we want to hide the entire .container
when the .results
div
is empty (since the container itself will never be empty). For scenarios like this, we can combine the the :empty
pseudo-class with :has()
, to hide any .container
that has an empty .results
div
:
.container:has(.results:empty) {
display: none;
}
<!-- This will be visible -->
<div class="container">
<h4>Suggestions</h4>
<div class="results">
<div>Result 1</div>
<div>Result 2</div>
<div>...</div>
</div>
</div>
<!-- This will be hidden -->
<div class="container">
<h4>Suggestions</h4>
<div class="results"></div>
</div>
Here, .container
selects all .container
s, and then :has()
filters them to only those that have an empty .results
div
. Note that .has()
is only supported by 84.68%
of all major browsers, and you may want to use a polyfill while awaiting broader support.
Hiding a parent element that doesn’t contain a certain child
You can equally choose to hide a container based on if it doesn’t contain a certain child, say a .result
. Imagine that our markup looks something like this, where we return a series of .result
divs:
<div class="container">
<h4>Suggestions</h4>
<div class="result">...</div>
<div class="result">...</div>
<div class="result">...</div>
</div>
For a scenario like this, we can combine the :not()
pseudo-class with :has()
to hide the .container
if it doesn’t contain any .result
elements:
.container:not(.container:has(.result)) {
display: none;
}
<!-- This will be visible -->
<div class="container">
<h4>Suggestions</h4>
<div class="result">Result 1</div>
<div class="result">Result 2</div>
<div class="result">...</div>
</div>
<!-- This will be hidden -->
<div class="container">
<h4>Suggestions</h4>
</div>
Here, we start by selecting all .container
elements, then we exclude elements from that list with :not()
, and we exclude all .container
elements that contain a .result
. What remains is any .container
that doens’t include a .result
, and we use display: none
to hide it. Note that unlike :has()
, :not()
is actually supported by all major browsers, and can safely be used without a polyfill.
We might not be able to avoid toggling classes completely to handle states, but with the help of these patterns we can to a larger extent let our styles be a function of the content that’s being displayed, and build more robust experiences.