首页 > 编程笔记

CSS :empty伪类用法详解

先来了解一下 :empty 伪类的基本匹配特性。

1) :empty 伪类用来匹配空标签元素。例如:
<div class="cs-empty"></div>
.cs-empty:empty {
   width: 120px;
   padding: 20px;
   border: 10px dashed;
}
此时,:empty 伪类会匹配 <div> 元素,呈现为虚线框,如下图所示。


图 1 :empty伪类匹配<div>元素,呈现为虚线框

2) :empty 伪类还可以匹配前后闭合的替换元素,如 <button> 元素和 <textarea> 元素。例如:
<textarea></textarea>
textarea:empty {
   border: 6px double deepskyblue;
}
在所有浏览器下都呈现为双实线,如下图所示。


图 2 :empty伪类匹配<textarea>元素

在 IE 浏览器下,<textarea> 元素的 :empty 伪类匹配有一些不寻常的特性。

首先,如果输入文字,则 IE 浏览器认为 <textarea> 元素并非空标签,不会以 :empty 伪类匹配。例如,输入“文字”,结果在 IE 浏览器下 <textarea> 元素的边框样式从双实线还原成了初始状态,如下图所示。


图 3 IE浏览器下:empty伪类不匹配输入值的<textarea>元素

这种行为非常类似于目前还没有任何浏览器支持的 :blank 伪类。

其次,当 <textarea> 元素的 placeholder 属性值显示时,IE 浏览器也不会以 :empty 伪类匹配。例如,HTML代码如下:
<textarea placeholder="请输入姓名"></textarea>
其交互状态如下图所示。


图 4 IE浏览器下:empty伪类不匹配显示placeholder属性值的<textarea>元素

我们可以利用这个特性让 IE 浏览器模拟自身并不支持的 :placeholder-shown 伪类,具体设置如下:
<textarea placeholder="请输入姓名" required></textarea>
此时,textarea:not(:empty):invalid 选择器的行为就等同于 Chrome 等浏览器下的 :placeholder-shown 伪类的行为了,属于典型的“bug 变 trick”。

由于 IE 浏览器已经日薄西山,因此这个特性的用处其实并不大。

3) :empty 伪类还可以匹配非闭合元素,如 <input> 元素、<img> 元素和 <hr> 元素等。例如:
input:empty,
img:empty,
hr:empty {
   border: 6px double deepskyblue;
}
<input type="text" placeholder="请输入姓名">
<img src="./1.jpg">
<hr>
在所有浏览器中的效果如下图所示。


图 5 :empty伪类匹配非闭合元素

但实际开发中很少有需要使用 :empty 伪类匹配非闭合元素的场景。

对:empty伪类可能存在的误解

:empty 伪类可以匹配什么样的元素?如果没有深入研究,你大概会认为 :empty 伪类可以匹配没有任何子元素、不显示任何内容的元素。但如果深入细节,就会发现这其中存在误解。

1) :empty伪类与空格

如果元素内有注释,:empty 伪类是否可以匹配?多数人会觉得不匹配,这是完全正确的。例如:
<!-- 无法匹配:empty伪类 -->
<div class="cs-empty"><!-- 注释 --></div>
但如果元素内有一个空格或者标签内有换行符呢?这时很多人就会有错误的认识了。实际上,:empty 伪类依然无法匹配。

例如,有以下两种情况:
①、不能有空格:
<!-- 无法匹配:empty伪类 -->
<div class="cs-empty">  </div>
②、不能有换行符:
<!-- 无法匹配:empty伪类 -->
<div class="cs-empty">
</div>
因此,实际开发的时候,如果遇到 :empty 伪类匹配无效的场景,要仔细查看 HTML 代码,看看标签内是否有空格或者换行符。尤其是使用一些渲染模板的时候,明明没有任何列表内容,但 :empty 伪类就是无法匹配,这可能是换行符或者空格导致的。

不过根据具体实践,一些流行的开发框架(如 Vue 等)会自动去除空格,这有利于在实际项目中灵活使用 :empty 伪类。

:empty 伪类忽略空格的特性不符合我们的直观认知,W3C 官方也收集到了很多这样的意见,所以在 CSS 选择器 Level 4 规范中已经开始明确 :empty 伪类可以匹配只有空格文本节点的元素,但是直到撰写本节的时候还没有任何浏览器支持。因此,为安全起见,实际开发中大家还是按照无空格标准来进行。

Firefox 浏览器中有一个私有伪类可以使元素匹配空标签元素或带有空格的标签元素,这个伪类就是 -moz-only-whitespace。例如:
.cs-empty:-moz-only-whitespace {
   border: 10px dotted;
}
可以匹配:
<!-- Firefox可以匹配:empty伪类 -->
<div class="cs-empty">  </div>
但毕竟 Firefox 浏览器的市场份额有限,大家了解即可。

最后一点,对于没有闭合标签的闭合元素,:empty 伪类也无法匹配,浏览器会自动补全 HTML 标签。例如,段落元素可以直接写成:
<p>段落
<p>段落
<p>段落
这样写解析没有任何问题。下面问题来了,如果标签里面没有任何其他内容,例如:
<p class="cs-empty">
<p class="cs-other">
:empty伪类也无法匹配.cs-empty:
<!-- .cs-empty无法匹配:empty伪类 -->
<p class="cs-empty">
<p class="cs-other">
因为浏览器自动补全的内容将一直延伸到下一个标签元素的开头,所以这里的 .cs-empty 元素实际上包含了换行符,等同于下面这种写法:
<p class="cs-empty">
</p><p class="cs-other">
也可以使用 JavaScript 验证上面的结论:
document.querySelector('.cs-empty').innerHTML
// 结果是回车符 ↵

因此,如果想要 :empty 伪类匹配自动补全标签,其需要首尾相连:
<!-- .cs-empty可以匹配:empty伪类 -->
<p class="cs-empty"><p
class="cs-other">

2) :empty伪类与::before/::after伪元素

::before 和 ::after 伪元素可以给标签插入内容、图形,但这样会不会影响 :empty 伪类的匹配呢?

答案是:不会。例如:
.cs-empty::before {
   content: '我是一段文字';
}
.cs-empty:empty {
   border: 10px dotted deepskyblue;
}
<!-- 可以匹配:empty伪类 -->
<div class="cs-empty"></div>
虽然我们在 .cs-empty 的元素内部插入了一段文字,但是浏览器依然按照 :empty 伪类进行了渲染,如下图所示。


图 6 应用了::before伪元素,但:empty伪类依然匹配

这一特性非常实用。

超实用超高频使用的:empty伪类

无论是大项目还是小项目,都会用到 :empty 伪类。主要有下面几种场景。

1) 隐藏空元素

例如,某个模块里面的内容是动态的,其可能是列表,也可能是按钮,这些模块容器常包含影响布局效果的 CSS 属性,如 margin、padding 属性等。当然,这些模块里面有内容的时候,布局显示效果是非常好的。然而,一旦这些模块里面的内容为空,页面上就会有很大一块明显的空白,布局效果就不好,这种情况下使用 :empty 伪类予以控制就再好不过了:
.cs-module:empty {
   display: none;
}
无须额外的 JavaScript 逻辑判断,直接使用 CSS 就可以实现动态样式效果。唯一需要注意的是,当列表内容缺失的时候,一定要把空格也去掉,否则 :empty 伪类不会匹配。

2) 字段缺失智能提示

例如,下面的 HTML 代码:
<dl>
   <dt>姓名:</dt>
   <dd>张三</dd>
   <dt>性别:</dt>
   <dd></dd>
   <dt>手机:</dt>
   <dd></dd>
   <dt>邮箱:</dt>
   <dd></dd>
</dl>
用户某些字段的信息是缺失的,此时开发人员应该使用其他占位符示意这里没有内容,例如用短横线(-)或者直接使用文字提示。但多年的开发经验表明,开发人员非常容易忘记这里需要的特殊处理,最终导致布局混乱,信息难辨。
/* <dd>为空布局会混乱 */
dt {
   float: left;
}
但如今,我们不用再担心这样的问题了,直接使用 CSS 就可以处理这种情况,代码很简单:
dd:empty::before {
   content: '暂无';
   color: gray;
}
此时字段信息缺失后的布局效果如下图所示。


图 7 空字段借助:empty伪类和::before伪元素占位

可以看到,这样的布局效果良好,信息明确。存储的是什么数据内容,直接输出的就是什么内容,就算数据库中存储的是空字符也无须担心。

实际开发中,类似的场景还有很多。例如,表格中的备注信息通常都是空的,此时可以这样处理:
td:empty::before {
   content: '-';
   color: gray;
}
除此之外,还有一类典型场景需要用到 :empty 伪类,那就是 Ajax 动态加载数据为空的情况。当一个新用户开始使用一个产品的时候,很多模块内容是没有的。要是在过去,我们需要在 JavaScript 代码中做 if 判断,如果没有值,我们要输出“没有结果”或者“没有数据”的信息。但是现在有了 :empty 伪类,直接把这个工作交给 CSS 就可以了。例如:
.cs-search-module:empty::before {
   content: '没有搜索结果';
   display: block;
   line-height: 300px;
   text-align: center;
   color: gray;
}
又如:
.cs-article-module:empty::before {
   content: '您还没有发表任何文章';
   display: block;
   line-height: 300px;
   text-align: center;
   color: gray;
}
总之,这种方法非常好用,可以节约大量的开发时间,同时用户体验更好,维护更方便,因为可以使用一个通用类名使整站提示信息保持统一:
.cs-empty:empty::before {
   content: '暂无数据';
   display: block;
   line-height: 300px;
   text-align: center;
   color: gray;
}

推荐阅读