问题
假如要实现一个类似如下图的一个定义列表,每一行都是一个名值对。
HTML 代码结构如下:
<dl>
<dt>Name:</dt>
<dd>Musk</dd>
<dt>Email:</dt>
<dd>user@domain.com</dd>
<dt>Location:</dt>
<dd>Earth</dd>
</dl>
在没有添加自定义样式前,定义列表默认的样式如下图:
显然,由于 <dt>
和 <dd>
由于是块级元素,所有的名和值均独占一行。要让 <dt>
和 <dd>
在同一行显示,很容易想到把它们都改成行内元素。CSS 如下:
dt, dd {
display: inline;
}
dd {
margin: 0;
font-weight: bold;
}
通过 display:inline
把两者改成行内元素,以及修改了一些默认样式后,结果定义列表变成如下图所示:
显然,我们在每个 <dd>
标签后面还缺一个换行,还未完全实现,那么接下来会探讨如何去插入换行。
如何插入换行
首先 ,如果我们不在乎使用 <br>
标签进行换行,那么最直接的方法就是在每个 <dd>
标签后面添加一个 <br>
标签进行换行。比如这样:
...
<dt>Name:</dt>
<dd>Musk<br /></dd>
...
上面的这个方法显然过于简单粗暴,后期的可维护性也不好,那有没有其他的方法?
实际上,有一个 Unicode 字符是专门代表换行符的:0x000A
。在 CSS 中,这个字符可以写作 "\000A"
,或简化为 "\A"
。我们可以把其作为 ::after
伪元素的内容,添加到每个 <dd>
元素的尾部,代码如下:
dd::after {
content: "\A";
}
但是,我们把这段代码尝试运行之后,发现并没有变化。这是为什么呢?这段代码所做的实际是在 HTML 结构中的所有关闭标签 </dd>
之前添加了换行符,但在 HTML 中,这些换行符会与相邻的其他空白符进行合并。空白符合并通常是一件好事情,否则我们要把 HTML 文档的源代码整理进一行。但有时候我们希望保留源代码中的一些空白符和换行。这里我们就需要这么处理,通常会使用 white-space: pre;
。这里我们也是这么做,但只对伪元素生成的换行符设置这个样式。代码如下:
dt, dd {
display: inline;
}
dd {
margin: 0;
font-weight: bold;
}
dd::after {
content: "\A";
white-space: pre;
}
这时候亲手测试一下,发现这个办法渲染的定义列表结果跟想要实现的效果一模一样。但这个这个方法还不够健壮,假设定义列表中的 <dt>
对应多个 <dd>
,那么这多个 <dd>
标签后都会插入换行,显得不太好。
...
<dt>Email:</dt>
<dd>user@gmail.com</dd>
<dd>user@hotmail.com</dd>
...
这时我们可能会想到:不将换行符放在 <dd>
后面,而是放在 <dt>
前面:
dt::before {
content: "\A";
white-space: pre;
}
但这会导致第一行变为空行,为了规避这个问题,可以尝试使用以下这些选择符来替代单纯的 dt
:
- dt:not(:first-child)
- dt ~ dt
- dd + dt
最后我们采用最后一种方法,因为假设多个 <dt>
对应一个值的场景下,它也可以正常显示,另外两者在就会有问题。除此之外,我们还可以在多个 <dd>
之间插入一个逗号,那么最终 CSS 代码如下:
dd + dt::before {
content: "\A";
white-space: pre;
}
dd + dd::before {
content: ", ";
font-weight: normal;
}
最终的渲染效果如下: