CSS伪选择器::empty vs :blank

发布于 大漠

前段时间@Zell Liew的博客深入的介绍了CSS中伪选择器:empty:blank相关的知识。那么:empty:blank究竟有什么作用,又在什么场景下使用呢?今天我们来一起看看,它们怎么使用,更应该应用在哪个场景之下呢?

使用场景

我们平时做项目的时候,时常会使用一些CSS Frameworks,比如说有名的Bootstrap,在这种情况之下,使用的一些选择器,是带有一些默认样式。有的时候元素中并没有任何内容,那么在页面上显示的效果就有点怪异。比如下面这样的一个场景,你的页面上有两个.alertdiv应用,其中一个有内容,另外一个任何内容都没有(就一个空div)。比如:

<div class="alert alert-success">Success Alert</div>
<div class="alert alert-info"></div>

如果你的页面中引用了bootstrap.css的话,这个时候看到的效果将会是这样:

对于这样的效果,估计没有人能接受。那么为什么会这样呢?来看一下代码,不难发现.alert有一个默认的样式:

.alert {
    position: relative;
    padding: .75rem 1.25rem;
    margin-bottom: 1rem;
    border: 1px solid transparent;
    border-radius: .25rem;
}

元素中主要有一个padding,撑大了元素容器,就算元素没有任何内容,该元素也有一定的大小。就如上面看到的效果。那么我们有没有别的方法能处理呢?毕竟很多场景我们是不好控制元素的。

既然标题提到了:empty:blank,估计有同学想到了解决方案是不是和这两个伪元素有关呢?如果感兴趣的话,可以接着往下阅读,在介绍相应的解决方案之前,咱们先来了解一下:empty:blank

:empty:blank

首先,:empty是什么?:blank`又是什么?

简单的说,** :empty:blank 都是CSS的伪选择器。其中:empty可以让你选择 空元素 。空元素是指没有任何内容的元素,甚至空格都不行。即:

<div class="alert alert-info"></div>

空元素可以有注释,只要注释填满了整个元素:

<!-- 完元素 -->
<div class="alert alert-info"><!-- 我是一个注释 --></div>

<!-- 非空元素 -->
<div class="alert alert-info"> <!-- 我是一个注释 --></div>
<div class="alert alert-info"><!-- 我是一个注释 --> </div>
<div class="alert alert-info"> <!-- 我是一个注释 --> </div>

最上面的一行代码,虽然div中有注释,但<div></div>之间没有任何内容,甚至是空格都没,那么他就是一个空元素,但下面三个div,虽然包含的也是注释,但是里面含有空格。所以说,这三个div不是空元素。

:blank:empty为灵活。它可以让你选择有空白的元素:

  • 没有子节点
  • 仅有空的文本节点
  • 仅有空白符的文本节点

比如:

<div class="alert alert-info"> </div>

不过从字面上来理解,他们都指的是 。在实际的运用之中,不管是:empty:blank都是有用的,比如说:

  • 给空元素添加样式
  • 创建空的状态

空元素的样式

比如我们一个操作,只有接口正常的时候才有数据输出,当接口失败的时候,没有数据输出。另外拿大家最为熟悉的一个示例来举例: 表单控件:

<input type="email" >
<div class="block-help"></div>

当表单验证失败的时候,div.block-help才有数据输出。在这里,我们对.block-help有一个样式的设置,特别是,当这个.block-help带有物理尺寸相关的样式时,就会直接影响页面的效果,比如文章开头提到的那个示例。在没有:empty:blank的时候,我们需要给这个div添加一个类名或通过HTML的属性来控制。比如:

.block-help {
    padding: .5em;
    display: none;
}

.block-help.success {
    display: block
}

如果使用:empty就不需要添加额外的类名或HTML元素属性。甚至display属性都可以省了。比如,文章最开头的.alert示例:

.alert:empty {
    padding: 0;
    border: none;
}

再来看另一场景,稍微复杂一顶点,假设我们的项目中有这样的一个效果:

这个场景相对于文章开头的示例要复杂一些。

<div class="blue"></div>
<div class="box">
    <div class="lime"></div>
    <div class="red"></div>
</div>

div.lime有内容的时候,div.red距离div.blue更远(margin-top),而当div.red如果没有字段输出的时候,div.red距离div.blue更近。处理这样的两个场景,:empty能灵巧的多了。

/* 实现左图的效果 */
.blue{
    width: 750px;
    height: 50px;
    border: 5px solid blue;
}
.lime {
    width: 100px;
    height: 50px;
    border: 5px solid lime;
}
.red {
    width: 750px;
    height: 300px;
    border: 5px solid red;
    margin-top: 30px;
}

/* 实现右图的效果 */
.lime:empty {
    display: none;
}
.lime:empty + .red {
    margin-top: 10px;
}

是不是觉得轻松多了。

创建空状态

在@Zell Liew的文章中举了一个待办事项列表。当你的用户第一次看到待办事项列表时,他们可能会看到没有任何待办事项。当没有任何待办事项,显示另一个效果向你的用户展示。而这个没有任何待办事项的状态就是我们所说的空状态

写这样的效果,其结构可以像下面这样:

<ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
</ul>
<div class="empty-state"></div>

没有任何待办事项,对应的结构就变成这样了:

<ul></ul>
<div class="empty-state"></div>

那么这个时候,我们也可以使用上例的方法来做相应的效果。使用:empty伪选择器和相邻的兄弟选择器E + F或后续的兄弟选择器E ~ F来对.empty-state做样式上的处理:

.empty-state {
    display: none;
}

ul:empty + .empty-state {
    display: block;
}

@Heydon的写的ToDoList组件就运用了这方面的技术。感兴趣的阅读@Heydon写的文章

:empty足够了?

既然:empty这么优秀,是不是:empty就足够了呢?事实上并非如此。原因有二:

  • 糟糕的开发者体验
  • 需要借用JavaScript手动清理元素中的空白

第一条还无所谓,但第二条为什么呢?

不知道大家是否有注意到,很多时候我们使用JavaScript框架来控制数据输出时,有可能造成元素内容是空的,但有可能无法确定里面有没有包含空格。或者写HTML结构的时候,手抖了一下,元素中包含了空格符。面对这样的场景,:empty就歇菜了。

还是拿@Zell Liew文章的待办事项来举例。

示例的结构如下:

<ul>
    <li>Item 1</li>
</ul>
<div class="empty-state"></div>

为了让:empty可以正常工作,我们需要从ul中删除所有的li。实现这样的效果并不难,使用JavaScript的removeChild就可以达到我们所要的需求:

const ul = document.querySelector('ul')
const li = ul.children[0]

ul.removeChild(li)

removeChild是可以将元素删除,但它却会生成包含空格的HTML(即使它可能不会在浏览器的检查器中显示)。我们可以使用childNodes属性来检查文本节点是否存在。可以在浏览器模拟一下这样的场景:

因此在ul.children.length === 0的时候,使用ul.innerHTML=''清理<ul>中的空格。整个代码如下所示:

const ul = document.querySelector('ul')
const li = ul.children[0]

ul.removeChild(li)

if (ul.children.length === 0) {
    ul.innerHTML = ''
}

最终效果如下:

:blank来解围

就上面这个示例的场景而言,如果:blank得到浏览器支持的话,咱们就无需借助JavaScript来实现,完全可以使用:blank来完成:

ul + div {
    display: none;
}

ul:blank + div {
    display: block;
    background: hsl(240, 50%, 80%); 
    padding: 0.5em 0.75em; 
}

总结

:empty:blank可以让你轻松地设计空元素样式并生成空状态。

:blank要比:empty更好,因为它为我们提供更有好的开发体验。只不过到写这篇文章为止,:blank还没有得到浏览器的支持。不过:empty已经足够好了。也足够我们使用了,只不过有的场景之下,我们要确保元素是真的空元素,不包括空格符哟。

文章中只是举了几个简单的示例,其实还有其他的使用场景,如果你有这方面的经验,欢迎在下面的评论中与我们一起共享。Nike Zoom Vomero 14