前段时间@Zell Liew的博客深入的介绍了CSS中伪选择器:empty和:blank相关的知识。那么:empty和:blank究竟有什么作用,又在什么场景下使用呢?今天我们来一起看看,它们怎么使用,更应该应用在哪个场景之下呢?
使用场景
我们平时做项目的时候,时常会使用一些CSS Frameworks,比如说有名的Bootstrap,在这种情况之下,使用的一些选择器,是带有一些默认样式。有的时候元素中并没有任何内容,那么在页面上显示的效果就有点怪异。比如下面这样的一个场景,你的页面上有两个.alert的div应用,其中一个有内容,另外一个任何内容都没有(就一个空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中有注释,但
:blank较:empty为灵活。它可以让你选择有空白的元素:
没有子节点
仅有空的文本节点
仅有空白符的文本节点
比如:
不过从字面上来理解,他们都指的是 空。在实际的运用之中,不管是:empty和:blank都是有用的,比如说:
给空元素添加样式
创建空的状态
空元素的样式
比如我们一个操作,只有接口正常的时候才有数据输出,当接口失败的时候,没有数据输出。另外拿大家最为熟悉的一个示例来举例: 表单控件:
当表单验证失败的时候,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.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的文章中举了一个待办事项列表。当你的用户第一次看到待办事项列表时,他们可能会看到没有任何待办事项。当没有任何待办事项,显示另一个效果向你的用户展示。而这个没有任何待办事项的状态就是我们所说的空状态。
写这样的效果,其结构可以像下面这样:
- Item 1
- Item 2
- Item 3
没有任何待办事项,对应的结构就变成这样了:
那么这个时候,我们也可以使用上例的方法来做相应的效果。使用: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文章的待办事项来举例。
示例的结构如下:
- Item 1
为了让: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=''清理
- 中的空格。整个代码如下所示:
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已经足够好了。也足够我们使用了,只不过有的场景之下,我们要确保元素是真的空元素,不包括空格符哟。
文章中只是举了几个简单的示例,其实还有其他的使用场景,如果你有这方面的经验,欢迎在下面的评论中与我们一起共享。