了解一些实用且富有创意的列表样式设置方法。
提到列表时,您会想到什么?最明显的例子是购物清单,它是最简单的清单,没有特定顺序的一系列商品。但我们在网络上会以各种方式使用列表。某个场馆即将举办的一系列音乐会?很可能是一个列表。多个步骤的预订流程?很可能是列表。图片库?这也可以视为带有图片说明的图片列表。
在本文中,我们将深入探讨 Web 上可用的不同 HTML 列表类型以及何时使用它们,包括您可能不熟悉的一些属性。我们还将介绍一些实用且富有创意的方法,以便使用 CSS 为它们设置样式。
何时使用列表
需要对项目进行语义分组时,应使用 HTML 列表元素。辅助技术(例如屏幕阅读器)会通知用户存在列表和项目数量。例如,如果您想在购物网站上展示一张商品网格,了解这些信息会非常有帮助。因此,使用列表元素可能是一个不错的选择。
列出类型
在标记方面,我们可以选择三种不同的列表元素:
- 无序列表
- 有序列表
- 说明列表
选择哪一个取决于用例。
无序列表 (ul)
当列表中的项没有任何特定顺序时,无序列表元素 (<ul>
) 最有用。默认情况下,这将显示为项目符号列表。例如,购物清单的顺序无关紧要。
在 Web 上,导航菜单就是一个更常见的示例。构建菜单时,最好将 ul
封装在 nav
元素中,并使用标签标识菜单,以便辅助技术使用。我们还应该标识菜单中的当前页面,可以使用 aria-current
属性执行此操作:
<nav aria-label="Main">
<ul>
<li>
<a href="/page-1" aria-current="page">Menu item 1</a>
</li>
<li>
<a href="/page-2">Menu item 2</a>
</li>
<li>
<a href="/page-2">Menu item 2</a>
</li>
…
</ul>
</nav>
这篇介绍菜单结构的文章列出了一些建议,可确保所有用户都能访问我们的导航菜单。
有序列表 (ol)
当项的顺序很重要时(例如多步流程),有序列表元素 (<ol>
) 是最佳选择。默认情况下,列表项会编号。例如,一组说明,其中的步骤必须按顺序完成。
<ol>
和 <ul>
元素只能包含 <li>
元素作为其直接子级。
说明列表 (dl)
说明列表包含术语(<dt>
元素)和说明(<dd>
)。每个术语可以有多个说明。可能的用例包括术语表,或者餐厅菜单。默认情况下,描述列表不会显示任何标记,但浏览器往往会缩进 <dd>
元素。
在 HTML 中,您可以使用 <div>
将术语及其随附的说明分组。这对于设置样式非常有用,我们稍后会加以说明。
<!-- This is valid -->
<dl>
<dt>Term 1</dt>
<dd>This is the first description of the first term in the list</dd>
<dd>This is the second description of the first term in the list</dd>
<dt>Term 2</dt>
<dd>This is the description of the second term in the list</dd>
</dl>
<!-- This is also valid -->
<dl>
<div>
<dt>Term 1</dt>
<dd>This is the first description of the first term in the list</dd>
<dd>This is the second description of the first term in the list</dd>
</div>
<div>
<dt>Term 2</dt>
<dd>This is the description of the second term in the list</dd>
</div>
</dl>
简单列表样式
列表最简单的用法之一是在一段正文中使用。通常,这些简单列表不需要精心设计样式,但我们可能需要在一定程度上自定义有序或无序列表的标记,例如使用品牌颜色,或为项目符号使用自定义图片。我们可以使用 list-style
和 ::marker
伪元素执行很多操作!
::marker
除了为列表标记提供一些基本样式外,我们还可以创建循环项目符号。下面,我们针对 ::marker
伪元素的 content
值使用了三个不同的图片网址,这为购物清单示例添加了手写风格(而不是为所有商品都使用同一张图片):
::marker {
content: url("/marker-1.svg") ' ';
}
li:nth-child(3n)::marker {
content: url("/marker-2.svg") ' ';
}
li:nth-child(3n - 1)::marker {
content: url("/marker-3.svg") ' ';
}
自定义计数器
对于某些有序列表,我们可能希望使用计数器值,但要将另一个值附加到该值。我们可以将 list-item
计数器用作标记的 content
属性的值,并附加任何其他内容:
::marker {
content: counter(list-item) '🐈 ';
}
我们的计数器会自动递增 1,但我们也可以通过在列表项上设置 counter-increment
属性,允许它们递增其他值。例如,这将使计数器每次增加 3:
li {
counter-increment: list-item 3;
}
我们还可以深入探讨计数器的更多内容。CSS 列表、标记和计数器一文更详细地解释了其中一些可能的方法。
::标记样式设置的限制
有时,我们可能想要更好地控制标记的位置和样式。例如,您无法使用 Flexbox 或网格来定位标记,这在某些情况下可能会成为缺点,因为您可能需要其他对齐方式。::marker
公开了有限数量的 CSS 属性来设置样式。如果我们的设计需要基本样式以外的其他内容,我们最好使用其他伪元素。
为看起来不像列表的列表设置样式
有时,我们可能希望以与默认样式完全不同的方式设置列表样式。导航菜单通常就是这种情况,例如,我们通常希望移除所有标记,并可能使用 flexbox 水平显示列表。常见做法是将 list-style
属性设为 none
。这意味着无法再在 DOM 中访问标记伪元素。
使用 ::before 创建自定义标记
在 ::marker
出现之前,设置 ::before
伪元素的样式是创建自定义列表标记的常用方法。但即便是现在,它也能在我们需要时为我们提供更大的灵活性,以实现视觉复杂的列表样式。
与 ::marker
一样,我们可以使用 content
属性添加自己的自定义项目符号样式。与使用 ::marker
不同,我们需要进行一些手动定位,因为我们无法获得 list-style-position
提供的自动定位优势。不过,我们可以使用 Flexbox 相对轻松地对其进行定位,并且它确实提供了更多对齐方式。例如,我们可以更换标记的位置:
如果我们使用 ::before
元素为有序列表设置样式,可能还希望使用计数器添加数字标记。
li::before {
counter-increment: list-item;
content: counter(list-item);
}
使用 ::before
(而非 ::marker
)可让我们完全访问 CSS 属性来设置自定义样式,并允许动画和过渡(对 ::marker
的支持有限)。
列出属性
有序列表元素接受一些可选属性,这些属性可帮助我们在各种使用情形中发挥作用。
反向列表
如果我们有一个过去一年的十大专辑列表,则可能需要从 10 到 1 进行倒计数。我们可以为此使用自定义计数器,并以负数递增计数器。或者,我们也可以直接在 HTML 中使用 reversed
属性。我认为,除非计数器完全用于呈现目的,否则通常使用 reversed
属性,而不是在 CSS 中负增计数器,这样更有语义意义。如果 CSS 未能加载,您仍然会在 HTML 中看到数字正确倒计时。此外,我们还需要考虑屏幕阅读器会如何解读列表。
请参阅此演示,了解 2021 年十大专辑。如果只使用 CSS 增加计数器,那么使用屏幕阅读器访问页面的用户可能会得出以下结论:数字在增加,因此 10 实际上是数字 1。
您可以在演示中看到,通过使用 reversed
属性,我们的标记已经具有正确的值,而我们无需额外付出任何努力!但是,如果我们使用 ::before
伪元素创建自定义列表标记,则需要调整计数器。我们只需指示列表项计数器以负值递增即可:
li::before {
counter-increment: list-item -1;
content: counter(list-item);
}
在 Firefox 中,这已经足够了,但在 Chrome 和 Safari 中,标记将从 0 倒计时到 -10。我们可以通过向列表中添加 start
属性来解决此问题。
拆分列表
借助 start
属性,我们可以指定列表应从哪个数字值开始。这在以下情况下非常有用:您想将列表拆分为多个组。
让我们以 10 大专辑为例。也许我们实际上想按 10 个为一组的方式统计前 20 张专辑。在这两个组之间还有一些其他网页内容。
我们需要在 HTML 中创建两个单独的列表,但如何确保计数器的正确性?目前的标记方式是,这两个列表都会从 10 倒计到 1,这并不是我们想要的。不过,我们可以在 HTML 中指定 start
属性值。如果我们为第一个列表添加了 20 的 start
值,标记将再次自动更新!
<ol reversed start="20">
<li>...</li>
<li>...</li>
<li>...</li>
</ol>
多列列表布局
正如之前的演示所示,多列布局有时很适合我们的列表。通过设置列宽,我们可以确保列表能够自动响应,只有在有足够的空间时,列表才会超过两列或更多列。我们还可以设置列之间的间距,并为增添美感而添加带样式的column-rule(使用与 border
属性类似的简写形式):
ol {
columns: 25rem;
column-gap: 7rem;
column-rule: 4px dotted turquoise;
}
使用列时,列表项中有时会出现不美观的换行,这并不总是我们想要的效果。
我们可以对列表项使用 break-inside: avoid
来防止这些强制换行:
li {
break-inside: avoid;
}
自定义属性
CSS 自定义属性为列表样式设置提供了各种可能性。如果我们知道列表项的索引,就可以使用它来计算属性值。遗憾的是,目前无法仅通过 CSS 来确定元素的索引(至少无法以可用的方式确定)。计数器仅允许我们在 content
属性中使用其值,而不允许进行计算。
不过,我们可以在 HTML 的 style
属性中设置元素的索引,这可以使计算更可行,尤其是在使用模板语言时。以下示例展示了如何使用 Nunjucks 进行设置:
<ol style="--length: items|length">
</ol>
Splitting.js 是一个在客户端执行类似功能的库。
使用自定义属性值,我们可以通过多种方式显示列表中的进度。一种方式可以是使用步骤列表的进度条。在此示例中,我们使用带有线性渐变的伪元素为每个项创建一个条状标签,以显示用户在列表中浏览了多少内容。
li::before {
--stop: calc(100% / var(--length) * var(--i));
--color1: deeppink;
--color2: pink;
content: '';
background: linear-gradient(to right, var(--color1) var(--stop), var(--color2) 0);
}
我们还可以使用 hsl()
颜色函数,随着列表的滚动调整色相。我们可以使用自定义属性计算 hue
值。
说明列表样式
如前所述,我们可以选择将术语及其定义封装在 dl
中的 div
中,以便获得更多样式选项。例如,我们可能希望以网格形式显示列表。在列表中设置 display: grid
而不用封装容器 div
将每个组括起来,意味着我们的术语和说明会放在不同的网格单元格中。有时,这很有用,如以下示例所示,显示了馅饼菜单及其说明。
我们可以在列表本身上定义网格,并确保术语和说明始终按列对齐,列宽由最长的术语决定。
另一方面,如果我们想以卡片样式将术语及其说明明确分组,封装容器 <div>
会非常有用。
资源
- 列表列表和 ::marker 简介
- 使用 ::marker 的自定义标记
- 带有计数器的 CSS 列表