CSS伪选择器::empty vs :blank
前段时间@Zell Liew的博客深入的介绍了CSS中伪选择器:empty
和:blank
相关的知识。那么:empty
和:blank
究竟有什么作用,又在什么场景下使用呢?今天我们来一起看看,它们怎么使用,更应该应用在哪个场景之下呢?
使用场景
我们平时做项目的时候,时常会使用一些CSS Frameworks,比如说有名的Bootstrap,在这种情况之下,使用的一些选择器,是带有一些默认样式。有的时候元素中并没有任何内容,那么在页面上显示的效果就有点怪异。比如下面这样的一个场景,你的页面上有两个.alert
的div
应用,其中一个有内容,另外一个任何内容都没有(就一个空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