CSS 列表和计数器模块 第3级

W3C 工作草案,

此版本:
https://www.w3.org/TR/2020/WD-css-lists-3-20201117/
最新发布版本:
https://www.w3.org/TR/css-lists-3/
编辑草案:
https://drafts.csswg.org/css-lists-3/
先前版本:
问题跟踪:
CSSWG 问题库
在文档中
编辑:
Elika J. Etemad / fantasai (特邀专家)
Tab Atkins (谷歌)
前任编辑:
(谷歌)
(曾任微软)
建议编辑此规范:
GitHub 编辑器
贡献者:
Simon Montagu, AOL-TW/Netscape, smontagu@netscape.com
Daniel Yacob, yacob@geez.org
Christopher Hoess, choess@stwing.upenn.edu
Daniel Glazman, AOL-TW/Netscape, glazman@netscape.com

摘要

本模块包含与列表计数器相关的CSS特性:对它们进行样式设置、定位和操作其值。

CSS 是一种用于描述结构化文档(如HTML和XML)在屏幕、纸张等上的呈现语言。

本文件的状态

本节描述本文件在发布时的状态。其他文档可能会取代本文件。当前W3C发布的文档和最新的技术报告修订列表可以在W3C技术报告索引找到。

本文件由CSS工作组发布为工作草案。作为工作草案的发布并不意味着W3C会员的认可。

这是一个草稿文件,可能会随时更新、替换或被其他文档取代。引用本文件时不应当将其视为工作进展。

请通过在GitHub上提交问题(首选)发送反馈,标题中包括规范代码“css-lists”,例如:“[css-lists] …评论摘要…”。所有问题和评论都会被归档。或者,可以将反馈发送至(归档)的公共邮件列表www-style@w3.org。

本文件受2020年9月15日W3C过程文件的约束。

本文件由一个遵循W3C专利政策的团体生成。W3C维护一个公共专利披露列表,列出了与该小组的交付物相关的任何专利披露;该页面还包括专利披露的说明。任何实际了解其认为包含核心权利要求的专利的人,必须按照W3C专利政策第6节的规定披露该信息。

1. 引言

本规范定义了 ::marker 伪元素, 生成标记的 列表项 显示类型, 以及几个控制标记位置和样式的属性。

它还定义了 计数器, 计数器是特殊的数字对象, 通常用于生成标记的默认内容。

例如,以下示例演示了如何使用标记为每个编号列表项添加括号:
<style>
li::marker { content: "(" counter(list-item, lower-roman) ")";
}
li { display: list-item; }
</style>
<ol>
  <li>这是第一项。
  <li>这是第二项。
  <li>这是第三项。
</ol>

这将产生类似于以下内容:

  (i) 这是第一项。
 (ii) 这是第二项。
(iii) 这是第三项。

注意: 请注意,这个示例的内容比在HTML中通常需要的内容要冗长得多, 因为用户代理的默认样式表处理了大部分必要的样式。

通过后代选择器和子选择器, 可以根据嵌套列表的深度指定不同的标记类型。

1.1. 值定义

本规范遵循 CSS属性定义约定, 来自 [CSS2], 使用 值定义语法, 来源于 [CSS-VALUES-3]。 本规范未定义的值类型在 CSS 值与单位[CSS-VALUES-3]中定义。 与其他CSS模块结合可能会扩展这些值类型的定义。

除了在其定义中列出的属性特定值之外, 本规范定义的所有属性 也接受 CSS广域关键字 作为它们的属性值。 为了可读性,它们没有被显式重复。

2. 声明列表项

列表项是任何其 display 属性设置为 list-item 的元素。 列表项 生成 ::marker 伪元素;没有其他元素这样做。 此外,列表项 自动递增一个隐含的 list-item 计数器(见 § 4.6 隐式列表项计数器)。

3. 标记

列表项 显示类型的定义特征是其 标记, 是帮助标示每个 列表项 在列表中开始的符号或序数。 在CSS布局模型中,列表项 标记由与每个 列表项 关联的 标记盒 表示。 此 标记 的内容可以通过在 列表项 上设置 list-style-type 和 list-style-image 属性来控制,并通过为其 ::marker 伪元素分配属性。

3.1. ::marker 伪元素

标记盒由 ::marker 伪元素生成,作为列表项的第一个子元素,位于 列表项 之前,::before 伪元素(如果元素上存在)。 它的内容由§ 3.2 生成标记内容中定义的内容填充。

在这个例子中,标记用于为被指定为“备注”的段落编号:
<style>
p {
    margin-left: 12 em;
} 
p.note {
    display: list-item;
    counter-increment: note-counter;
} 
p.note::
    marker {
    content: "备注 " counter(
    note-counter)
    ":"; 
}
</style> 
<p>
    这是本文档的第一段。
<p class="note">
    这是一篇非常简短的文档。
<p>
    这是结束。

它应该呈现为如下内容:

        这是第一段
        在这个文档中。

备注 1: 这是一个非常简短的
        文档。

        这是结束。
通过使用 ::marker 伪元素,列表的标记可以 独立于列表项文本进行样式设置:
<style>
p {
    margin-left: 8em }
    /* 为计数器留出空间 */
li {
    list-style-type: lower-roman;
}
li::
    marker {
    color: blue;
    font-weight:bold;
    }
</style>
<p>
    这是一个较长的前导段落 ...
<ol>
  <li>
    这是第一项。
  <li>
    这是第二项。
  <li>
    这是第三项。
</ol>
<p>
    这是一个较长的后续段落 ...

前面的文档应该呈现如下:

       这是一个较长的
       前导段落 ...

  i.   这是第一项。
 ii.   这是第二项。
iii.   这是第三项。

       这是一个较长的
       后续段落 ...

之前唯一的标记样式方法是通过继承;需要将所需的标记样式放在列表项上,然后在列表项实际内容周围的包装元素上还原该样式。

标记盒仅存在于列表项上:在任何其他元素上,::marker 伪元素的content 属性必须计算为none,这将抑制其创建。

3.1.1. 适用于 ::marker 的属性

所有属性都可以设置在::marker 伪元素上,并将具有计算值;但是,只有以下CSS属性实际上适用于标记盒:

预计未来的规范将扩展此属性列表;然而,目前标记盒布局尚未完全定义, 因此仅允许这些属性。

在标记盒中,其他属性在作者源的级联中不得 产生效果。用户代理可以将这些属性视为不适用,或者通过设置用户代理源 !important规则来强制其值。 然而,适用于文本的可继承属性可以设置在::marker伪元素上:这些属性将继承并对其文本内容生效。

适用于文本的属性示例,因此在::marker上声明时将应用于::marker的内容:

用户代理必须将以下规则添加到其默认样式表:

::marker, ::before::marker, ::after::marker {
  unicode-bidi: isolate;
  font-variant-numeric: tabular-nums;
  white-space: pre;
  text-transform: none;
}

注: 尽管 ::marker 伪元素可以表示 ::before 或 ::after 伪元素的标记盒, 但复合选择器 ::marker, 其展开为 *::marker [SELECTORS-4], 不会选择这些标记——作为来源元素的伪元素 需要在选择器中显式指定, 例如 ::before::marker。

white-space: pre 的行为并不完全合适;text-space-collapse: preserve-spaces + text-space-trim: discard-after 可能更接近此处所需的行为。 参见 议题 4448 和 议题 4891 中的讨论。

3.2. 生成标记内容

标记盒marker box的内容由第一个真实的条件决定:

content 在 ::marker 本身上的值不是 normal
标记盒的内容 按照 content 属性的定义确定, 与 ::before 完全相同。
list-style-image 在来源元素上定义一个标记图像
'标记盒包含 一个表示指定标记图像的匿名行内替换元素, 后跟一个由单个空格(U+0020 SPACE)组成的文本运行。
list-style-type 在来源元素上定义一个标记字符串
标记盒包含 一个由指定标记字符串组成的文本运行。
否则
标记盒没有内容, 且 ::marker 不生成盒。

此外,用户代理可以将任何保留的强制换行转换为空格或丢弃。

3.3. 图像标记:list-style-image 属性

名称: list-style-image
值: <image> | none
初始值: none
适用对象: 列表项
继承: 是
百分比: 不适用
计算值: 关键字none或计算的<image>
标准顺序: 按语法
动画类型: 离散

指定了标记图像, 该图像用于填充列表项的 标记,当其内容为正常时。 可用值如下:

<image>
如果<image> 代表一个有效图像,则指定元素的标记图像为<image>。 否则,元素没有标记图像。
none
该元素没有标记图像。
以下示例将每个列表项开头的标记设置为图像“ellipse.png”。
li { list-style-image: url("http://www.example.com/ellipse.png") }

3.4. 基于文本的标记:list-style-type 属性

名称: list-style-type
值: <counter-style> | <string> | none
初始值: disc
适用对象: 列表项
继承: 是
百分比: 不适用
计算值: 指定值
标准顺序: 按语法
动画类型: 离散

指定了标记字符串, 该字符串用于填充列表项的标记,当其内容值为正常且没有标记图像时。 可用值如下:

<counter-style>
将元素的标记 字符串指定为 使用指定的 <counter-style> 表示的 list-item 计数器 的值。

具体而言, 标记字符串是 使用指定的 <counter-style> 对 list-item 计数器值生成计数器表示的结果, 前面加上该 <counter-style> 的 prefix, 后面加上该 <counter-style> 的 suffix。 如果指定的 <counter-style> 不存在,则假定为 decimal。

<string>
元素的标记 字符串是指定的 <string>。
none
元素没有标记 字符串。
以下示例演示了如何将标记设置为各种值:
ul { list-style-type: "★"; }
    /* 将标记设置为“星号”字符 */

p.note { 
  display: list-item; 
  list-style-type: "Note: "; 
  list-style-position: inside; 
} 
/* 为注释段落赋予标记,由字符串“Note: ”组成 */

ol { list-style-type: upper-roman; } 
/* 将所有有序列表设置为使用上罗马计数样式 */ 
   (在计数样式规范中定义 [[CSS-COUNTER-STYLES]]) */

ul { list-style-type: symbols(cyclic '○' '●'); } 
/* 设置所有无序列表项交替使用空心和实心圆作为标记。 */

ul { list-style-type: none; } 
/* 完全抑制标记,除非指定有效图像的list-style-image。 */ 

3.5. 标记位置:list-style-position 属性

名称: list-style-position
值: inside | outside
初始值: outside
适用对象: 列表项
继承: 是
百分比: 不适用
计算值: 关键字,但见正文
标准顺序: 按语法
动画类型: 离散

此属性决定::marker是以内联方式呈现, 还是定位在列表项的外部。 值如下:

inside
无特殊效果。 (::marker 是位于列表项内容开头的行内元素。)
outside
如果列表项是块容器: 则标记盒是一个块容器,并被放置在 主块盒之外; 但是,列表项标记与浮动相邻时的位置未定义。 CSS 未规定标记盒的精确位置 或其在绘制顺序中的位置, 但要求将其放置在盒的行内起始侧, 并使用由 marker-side 指定的盒的书写模式。 标记盒相对于主块盒的边框固定, 不会随主盒的内容滚动。 如果元素的 overflow 不是 visible,用户代理可以隐藏该标记。 (这一许可将来可能会发生变化。) 标记盒的尺寸或内容可能会影响 主块盒的高度 和/或其第一个行盒的高度, 并且在某些情况下可能导致创建新的行盒; 这种交互同样未定义。

这是 CSS2 中含糊其辞的 说法,需要一个真正的定义。

如果列表项是一个行内盒: 则此值等同于 inside。

或者,outside 可以将标记作为主行内盒的前一个 兄弟元素进行布局。

例如:
<style>
  ul.compact { list-style: inside; }
  ul         { list-style: outside; }
</style>
<ul class=compact>
  <li>第一个“inside”列表项</li>
  <li>第二个“inside”列表项</li>
</ul>
<hr>
<ul>
  <li>第一个“outside”列表项</li>
  <li>第二个“outside”列表项</li>
</ul>

上述示例可能会格式化为:

  * 第一个 "inside" 列表
  项位于最前
  * 第二个 "inside" 列表
  项位于第二

========================

* 第一个 "outside" 列表
  项位于最前
* 第二个 "outside" 列表
  项位于第二

3.6. 样式标记:list-style 简写属性

名称: list-style
值: <'list-style-position'> || <'list-style-image'> || <'list-style-type'>
初始值: 见各个属性
适用对象: 列表项
继承: 见各个属性
百分比: 见各个属性
计算值: 见各个属性
动画类型: 见各个属性
标准顺序: 按语法

属性list-style是用于设置三个属性的简写形式:list-style-type、list-style-image和list-style-position,可以在样式表中一次性设置。

例如:
ul { list-style: upper-roman inside }  /* 任何 UL */
ul ul { list-style: circle outside } /* 任何 UL 的子 UL */

在简写中使用none的值可能会产生歧义, 因为none是list-style-image和list-style-type的有效值。 为了解决这种歧义, 简写中的none值必须应用于未通过简写设置的两个属性中的一个。

list-style: none disc;
/* 将图像设置为 "none",类型设置为 "disc"。 */

list-style: none url(bullet.png);
/* 将图像设置为 "url(bullet.png)",类型设置为 "none"。 */

list-style: none;
/* 将图像和类型都设置为 "none"。 */

list-style: none disc url(bullet.png);
/* 语法错误 */

注意: <counter-style> 的值可以在list-style-type中创建语法歧义。 由于这些值最终是<custom-ident>值, 所以在[CSS-VALUES-3]中的解析规则适用。

尽管作者可以直接在列表项元素上指定list-style信息 (例如,HTML中的li), 他们应该谨慎行事。 考虑以下规则:
ol.alpha li { list-style: lower-alpha; }
ul li       { list-style: disc; }

上述示例不会按预期工作。 如果将一个ul 嵌套在一个ol class=alpha中, 第一个规则的特定性会使ul的 列表项使用lower-alpha样式。

ol.alpha > li { list-style: lower-alpha; }
ul > li       { list-style: disc; }

这些规则按预期工作。

ol.alpha { list-style: lower-alpha; }
ul       { list-style: disc; }

这些更好, 因为继承将把list-style值传递给列表项。

3.7. marker-side 属性

名称: marker-side
值: match-self | match-parent
初始值: match-self
适用对象: 列表项
继承: 是
百分比: 不适用
计算值: 指定的关键字
标准顺序: 按语法
动画类型: 离散

marker-side 属性指定了是否一个外部标记框的位置是基于列表项本身的方向性(即它的原始元素)还是列表容器的方向性(即原始元素的父元素)。在第一种情况下,标记的位置可以在同一列表的项目之间变化,基于每个列表项单独分配的方向性;在第二种情况下,所有标记将对齐在同一侧,由整体列表分配的方向性决定。

match-self
标记框 是使用::marker的原始元素的方向性来定位的。
match-parent
标记框 是使用::marker的原始元素的父元素的方向性来定位的。
默认情况下,元素或::marker伪元素根据其列表项的方向性自行定位。然而,如果列表项与多个可能具有不同方向性的其他列表项分组在一起(例如,在HTML中的多个具有不同"dir"属性的<li>),有时将所有标记排列在同一侧会更有用,因此作者可以在那一侧指定一个单一的“边距”,确保所有标记都位于该边距内并且可见。

以下两个示例渲染是由以下HTML生成的,唯一的区别是列表上的marker-side的值:

<ul>
  <li>英语一
  <li dir=rtl>阿拉伯语
  <li>英语三
  <li dir=rtl>阿拉伯语三
</ul>
match-self match-parent
* 英语一
     阿拉伯语 *
* 英语三
    阿拉伯语三 *
* 英语一
*    阿拉伯语
* 英语三
*   阿拉伯语三

为了使标点符号在标记内正确排序, 还需要考虑父元素的方向值。 <https://github.com/w3c/csswg-drafts/issues/4202>

有关重命名关键字和与 list-style-position 合并 的问题仍在开放中。

4. 使用计数器进行自动编号

计数器 是一个特殊的数字跟踪器,用于自动编号 CSS 中的列表项等多种用途。每个元素都有一个零或多个计数器的集合,这些计数器以类似于继承属性值的方式通过文档树继承。计数器 具有一个 名称 和 创建者,用于识别计数器,并且有一个整数 值。它们通过 计数器属性 counter-increment、counter-set 和 counter-reset 创建和操作,并与 counter() 和 counters() 函数表示法一起使用。

计数器在 CSS 语法中通过 <counter-name> 类型引用,表示其名称为 <custom-ident>。<counter-name> 名称不能与关键字 none 匹配;这样的标识符被认为是无效的 <counter-name>。

在给定元素上解析计数器值是一个多步骤的过程:

  1. 现有计数器从前面的元素继承。

  2. 新计数器被实例化(counter-reset)。

  3. 计数器值被递增(counter-increment)。

  4. 计数器值被显式设置(counter-set)。

  5. 计数器值被使用(counter()/counters())。

用户代理可能对计数器的最大值或最小值有实现特定的限制。如果计数器重置、设置或递增将使值超出该范围,则必须将值限制在该范围内。

4.1. 创建计数器:counter-reset 属性

名称: counter-reset
值: [ <counter-name> <integer>? ]+ | none
初始: none
适用对象: 所有元素
继承: 否
百分比: 不适用
计算值: 关键字 none 或一个列表,每个项是标识符和整数的配对
规范顺序: 按语法
动画类型: 按计算值类型

用户代理预计将在所有媒体上支持此属性,包括非视觉媒体。

counter-reset 属性实例化了元素上的新计数器,并将其设置为指定的整数值。其值定义如下:

none
此元素不创建任何新计数器。
<counter-name> <integer>?
实例化一个给定<counter-name>的计数器,其起始值为给定的<integer>,默认为0。
注意,计数器属性遵循层叠规则。因此,由于层叠,以下样式表:
h1 { counter-reset: section -1 }
h1 { counter-reset: imagenum 99 }

将仅重置imagenum。要重置两个计数器,它们必须一起指定:

H1 { counter-reset: section -1 imagenum 99 }

相同的原则适用于counter-set 和 counter-increment 属性。请参见[css-cascade-4]。

如果在属性值中出现多个相同的<counter-name> 实例,仅最后一个会被尊重。

4.2. 操作计数器值:counter-increment 和 counter-set 属性

名称: counter-increment
值: [ <counter-name> <integer>? ]+ | none
初始: none
适用对象: 所有元素
继承: 否
百分比: 不适用
计算值: 关键字 none 或一个列表,每个项是标识符与整数的配对
规范顺序: 按语法
动画类型: 按计算值类型

用户代理预计将在所有媒体上支持此属性,包括非视觉媒体。

名称: counter-set
值: [ <counter-name> <integer>? ]+ | none
初始: none
适用对象: 所有元素
继承: 否
百分比: 不适用
计算值: 关键字 none 或一个列表,每个项是标识符与整数的配对
规范顺序: 按语法
动画类型: 按计算值类型

用户代理预计将在所有媒体上支持此属性,包括非视觉媒体。

counter-increment 和 counter-set 属性用于操作现有计数器的值。只有在元素上不存在给定名称的计数器时,它们才会实例化新的计数器。它们的值定义如下:

none
此元素不会更改任何计数器的值。
<counter-name> <integer>?
为元素上命名的计数器设置(用于counter-set)或递增(用于counter-increment)其值,值为指定的<integer>。如果省略<integer>,则默认为1(用于counter-increment)或0(用于counter-set)。

如果元素上当前不存在给定名称的计数器,元素将实例化一个新计数器,起始值为0,然后设置或递增其值。

此示例演示了如何对章节和部分进行编号,格式为 "第1章"、"1.1"、"1.2" 等。

h1::before {
    content: "Chapter " counter(chapter) ". ";
    counter-increment: chapter;  /* Add 1 to chapter */
    counter-reset: section;      /* Set section to 0 */
}
h2::before {
    content: counter(chapter) "." counter(section) " ";
    counter-increment: section;
}

如果在属性值中出现多个相同的<counter-name>实例,它们会按顺序处理。因此,递增将累加,但只有最后一个设置值会生效。

4.3. 嵌套计数器和作用域

计数器是“自嵌套”的;在一个元素上实例化一个新的计数器, 如果该元素从其父元素继承了一个同名计数器, 则会创建一个同名的新计数器, 嵌套在现有计数器之内。 这对于 HTML 中的列表等情况非常重要, 因为列表可以任意深度地嵌套在其他列表中: 不可能为每一层定义唯一命名的计数器。 counter() 函数只获取元素上给定名称的最内层计数器, 而 counters() 函数则使用包含该元素的所有给定名称的计数器。

因此,一个计数器的作用域 从文档中第一个实例化该计数器的元素开始, 并包括该元素的后代及其后续兄弟元素和这些兄弟元素的后代。 但是,它不包括由该元素后续兄弟元素上的 counter-reset 创建的同名计数器作用域中的任何元素, 从而允许这种显式的计数器实例化 遮蔽那些较早兄弟元素中的计数器。

有关控制计数器作用域及其值的确切规则,请参见 § 4.4 创建和继承计数器。

以下代码为嵌套列表项编号。 其结果与在 LI 元素上设置 display:list-item 和 list-style: inside 非常相似:
ol { counter-reset: item }
li { display: block }
li::before { content: counter(item) ". "; counter-increment: item }

在此示例中, 一个 ol 将创建一个计数器, 并且该 ol 的所有子元素都将引用该计数器。

如果我们将 item 计数器的第 n个实例记为 itemn, 则以下 HTML 片段将使用所示的计数器。

<ol> 创建 item0,并将其设置为 0
<li> item0 递增至 1
<li> item0 递增至 2
<ol> 创建 item1,设置为 0,并嵌套在 item0 中
<li> item1 递增至 1
<li> item1 递增至 2
<li> item1 递增至 3
<ol> 创建 item2,设置为 0,并嵌套在 item1 中
<li> item2 递增至 1
</ol>
<li> item1 递增至 4
<ol> 创建 item3,设置为 0,并嵌套在 item1 中
<li> item3 递增至 1
</ol>
<li> item1 递增至 5
</ol>
<li> item0 递增至 3
<li> item0 递增至 4
</ol>
<ol> 创建 item4,并将其设置为 0
<li> item4 递增至 1
<li> item4 递增至 2
</ol>

4.4. 创建和继承计数器

文档中的每个元素或伪元素都具有 一个(可能为空的)计数器集合,这些计数器位于该元素的 作用域中, 它们要么继承自另一个元素, 要么直接在该元素上实例化。 这些计数器表示为一个 CSS 计数器集合, 它是一个集合,其中的每个值都是一个元组,包含: 一个字符串 (表示计数器的名称), 一个元素(表示计数器的创建者), 以及一个整数(表示计数器的值)。 该集合中具有给定名称的最后一个计数器 表示该名称的最内层 计数器。

4.4.1. 继承计数器

一个元素从其父元素和前一个兄弟元素继承其 初始计数器集合。 然后,它从其前一个树 顺序中的元素上匹配计数器的值中 获取这些计数器的值(该元素可能是其父元素、 其前一个兄弟元素, 或其前一个兄弟元素的后代)。 要将计数器继承 到一个 element 中:
  1. 如果 element 是其文档树的根, 则该元素具有一个初始为空的 CSS 计数器集合。 返回。

  2. 令 element counters 表示 element 自身的 CSS 计数器集合, 并使其为 element 父元素的 CSS 计数器集合的副本。

  3. 令 sibling counters 为 element 的前一个 兄弟元素(如果存在)的 CSS 计数器集合, 否则为空的 CSS 计数器集合。

    对于 sibling counters 中的每个 counter, 如果 element counters 尚未包含具有相同名称的计数器, 则将 counter 的副本追加到 element counters。

  4. 令 value source 为在树 顺序中紧接在 element 之前的元素的 CSS 计数器集合。

    对于 value source 中的每个 source counter, 如果 element counters 包含一个具有相同名称和创建者的计数器, 则将该计数器的值 设置为 source counter 的值。

以以下代码为例:
<ul style='counter-reset: example 0;'>
  <li id='foo' style='counter-increment: example;'>
    foo
    <div id='bar' style='counter-increment: example;'>bar</div>
  </li>
  <li id='baz'>
    baz
  </li>
</ul>

请记住,树顺序将文档树转换为有序列表,其中一个元素在其子元素之前,子元素在其下一个兄弟元素之前。换句话说,对于像HTML这样的语言,它是解析器在读取文档时遇到起始标签的顺序。

在这里,ul元素建立了一个名为example的新计数器,并将其值设置为0。#foo元素作为ul的第一个子元素,继承了此计数器。它的父元素也是其在树顺序中紧接着的元素,因此它继承了值0,然后立即将值递增到1。

对于#bar元素也是如此。它从#foo继承了example计数器,并且也继承了其值1,然后将其递增到2。

然而,#baz元素稍有不同。它从前一个兄弟元素#foo继承了example计数器。然而,与其一起继承的并不是从#foo获得的值1,而是从#bar,这是在树顺序中前一个元素获得的值2。

这种行为允许在整个文档中使用单个计数器,不断递增,而无需担心文档的嵌套结构。

注意:计数器继承与常规CSS 继承一样,在[DOM]上下文中对“扁平化元素树”操作。

4.4.2. 实例化计数器

计数器在 实例化时会在 counter-reset 中被命名, 并且在尚不存在时,如果在 counter-increment、counter-set,或者 counter() 或 counters() 记法中被命名,也会被实例化。 (新实例化的同名计数器会替换来自先前兄弟元素的同名计数器, 但会与来自祖先元素的同名计数器一同添加, 参见 § 4.3 嵌套计数器与作用域。) 要在 element 上以起始 value 实例化一个计数器,其给定名称为 name:
  1. 令 counters 为 element 的 CSS 计数器集合。

  2. 令 innermost counter 为 counters 中名称为 name 的最后一个计数器。 如果 innermost counter 的来源元素 是 element 或 element 的先前兄弟元素,则从 counters 中移除 innermost counter。

  3. 向 counters 追加一个新的计数器,其名称为 name, 来源元素为 element, 初始值为 value

4.5. 不生成盒子的元素中的计数器

不生成盒子的元素(例如,display 设置为 none,或一个伪元素的content 设置为 none)无法设置、重置或递增计数器。计数器属性在此类元素上仍然有效,但必须没有效果。

例如,使用以下样式表,类为“secret”的H2不会递增count2。
h2 { counter-increment: count2; }
h2.secret { display: none; }

注意:隐藏元素的其他方法,例如将visibility设置为hidden,仍然会导致元素生成一个盒子,因此这里不例外。

是否一个替换元素的后代(如HTML option,或SVG rect)可以设置、重置或递增计数器是未定义的。

注意:由于实现之间缺乏互操作性,对于替换元素后代的行为目前是未定义的。

4.6. 隐式 list-item 计数器

除了作者在其样式中显式定义的任何计数器之外,列表项还会自动递增 一个特殊的 list-item 计数器, 该计数器用于在列表项上生成默认的标记字符串(参见 list-style-type)。

具体而言, 除非 counter-increment 属性显式指定 list-item 计数器使用不同的递增量, 否则它必须在每个列表 项上递增 1, 并与计数器通常递增的时机 相同 (就如同在列表项的 counter-increment 值后追加了 list-item 1 一样, 包括可能实例化新的计数器等副作用)。 这不会影响 counter-increment 的指定值或计算 值。

由于每个列表项都会自动将 list-item 计数器递增 1, 因此具有数字 list-style-type 的连续列表项默认会连续编号——即使作者将 counter-increment 设置为其他值, 例如 counter-increment: itemnumber,甚至设置为 none。 这可以防止自动的 list-item 计数器 被原本用于处理其他计数器的声明 无意中覆盖。

但是,由于如果列表项的 counter-increment 显式提及 list-item 计数器,则自动的 list-item 递增不会发生,因此 li { counter-increment: list-item 2; } 会按指定将 list-item 递增 2, 而不是像无条件追加 list-item 1 时那样递增 3。

这也允许通过显式覆盖自动的 list-item 计数器递增来将其关闭, 例如 counter-increment: list-item 0;。

在所有其他方面,list-item 计数器的行为都与任何其他计数器一样, 作者可以使用和操作它 来调整列表项的样式 或用于其他目的。

在以下示例中, 列表被修改为每次递增 2:

ol.evens > li { counter-increment: list-item 2; }

一个包含三个列表项的列表将呈现为

2. 第一项
4. 第二项
6. 第三项

用户代理和宿主语言应确保 list-item 计数器值 默认 反映宿主语言语义所规定的底层数值, 以便在其用户代理样式表和表现提示样式映射中 设置列表项样式。 例如,参见 附录 A:HTML 示例样式表。

在以下示例中, 使用 content 属性创建分层编号, 该编号接入 list-item 计数器, 因而会遵循通过 HTML 引入的任何编号变化:
ol > li::marker { content: counters(list-item,'.') '.'; }

使用此规则的嵌套列表将呈现为

1. 第一个顶层项
5. 第二个顶层项,value=5
   5.3. 第一个第二层项,列表起始值=3
   5.4. 第二个第二层项,列表起始值=3
        5.4.4. 反向列表中的第一个第三层项
        5.4.3. 反向列表中的第二个第三层项
        5.4.2. 反向列表中的第三个第三层项
        5.4.1. 反向列表中的第四个第三层项
   5.5. 第三个第二层项,列表起始值=3
6. 第三个顶层项

对于如下标记

<ol>
  <li>第一个顶层项
  <li value=5>第二个顶层项,value=5
    <ol start=3>
      <li>第一个第二层项,列表起始值=3
      <li>第二个第二层项,列表起始值=3
        <ol reversed>
          <li>反向列表中的第一个第三层项
          <li>反向列表中的第二个第三层项
          <li>反向列表中的第三个第三层项
          <li>反向列表中的第四个第三层项
        </ol>
    </ol>
  <li>第三个第二层项,列表起始值=3
  <li>第三个顶层项
</ol>

4.7. 输出计数器:counter() 和 counters() 函数

计数器本身没有可见效果,但它们的值可以与counter()和counters()函数一起使用,这些函数的使用值以字符串或图像的形式表示计数器值。它们的定义如下:

<counter> = <counter()> | <counters()>
counter()  =  counter( <counter-name>, <counter-style>? )
counters() = counters( <counter-name>, <string>, <counter-style>? )

其中<counter-style>指定用于生成命名计数器的表示形式的计数器样式,正如生成计数器中定义的那样。

counter()
使用名为 <counter-style> 的计数器 样式,表示元素的CSS 计数器集合中名为 <counter-name> 的最内层计数器的值。
counters()
使用名为 <counter-style> 的计数器 样式,表示元素的CSS 计数器集合中所有名为 <counter-name> 的计数器的值, 按从最外层到最内层的顺序排序, 并使用指定的 <string> 连接。

在这两种情况下,如果<counter-style>参数被省略,则默认为decimal。

如果在使用 counter() 或 counters() 的元素上, 不存在名为 <counter-name> 的计数器, 则首先以 0 作为起始值实例化一个这样的计数器。

H1::before        { content: counter(chno, upper-latin) ". " }
/* 生成标题如 "A. A History of Discontent" */

H2::before        { content: counter(section, upper-roman) " - " }
/* 生成标题如 "II - The Discontent Part" */

BLOCKQUOTE::after { content: " [" counter(bq, decimal) "]" }
/* 生成以 "... [3]" 结尾的块引用 */

DIV.note::before  { content: counter(notecntr, disc) " " }
/* 在每个div.note之前简单生成一个项目符号 */

P::before         { content: counter(p, none) }
/* 插入无内容 */
以下示例展示了counters()函数的简单用法:
<ul>
  <li>一个</li>
  <li>两个
    <ul>
      <li>嵌套一个</li>
      <li>嵌套两个</li>
    </ul>
  </li>
  <li>三个</li>
</ul>
<style>
li::marker { content: '(' counters(list-item,'.') ') '; }
</style>

上述文档应渲染如下:

(1) 一个
(2) 两个
   (2.1) 嵌套一个
   (2.2) 嵌套两个
(3) 三个
由于计数器会遗传给兄弟元素,因此它们可以用来编号标题和子标题,而不需要相互嵌套。不幸的是,这会妨碍使用counters(),因为兄弟元素的计数器不嵌套,但可以创建多个计数器并手动连接它们:
<h1>第一个H1</h1>
...
<h2>第一个H2在H1中</h2>
...
<h2>第二个H2在H1中</h2>
...
<h3>第一个H3在H2中</h3>
...
<h1>第二个H1</h1>
...
<h2>第一个H2在H1中</h2>
...
<style>
body { counter-reset: h1 h2 h3; }
h1   { counter-increment: h1; counter-reset: h2 h3;}
h2   { counter-increment: h2; counter-reset: h3; }
h3   { counter-increment: h3; }
h1::before { content: counter(h1,upper-alpha) ' '; }
h2::before { content: counter(h1,upper-alpha) '.'
                      counter(h2,decimal) ' '; }
h3::before { content: counter(h1,upper-alpha) '.'
                      counter(h2,decimal) '.'
                      counter(h3,lower-roman) ' '; }
</style>

上述文档应渲染如下:

A. 第一个H1
...
A.1 第一个H2在H1中
...
A.2 第二个H2在H1中
...
A.2.i 第一个H3在H2中
...
B. 第二个H1
...
B.1 第一个H2在H1中
...
计数器有时可用于 输出标记之外的用途。 一般而言,它们提供了按顺序为元素编号的能力, 这对于供其他属性引用很有用。 例如,使用 order 将一个 元素放置在另外两个特定元素之间, 目前需要你在所需插入点之前和/或之后的 每个元素上显式设置 order。 但是,如果可以将所有元素的 order 值设置为一个 计数器, 就可以更轻松地将元素插入另外两个元素之间的任意位置。

其他使用场景涉及带有变换的嵌套元素或兄弟元素,这些变换旨在彼此 略有不同。 如今,要以合理的方式实现这一点,必须使用预处理器, 但计数器可以让它在“纯” CSS 中很好地工作。

(如今,在嵌套情况下可以通过使用自定义属性并逐层嵌套 calc(), 来逐步构造连续的值, 但这有*一点*笨拙, 而且不适用于兄弟元素。)

建议添加一个 counter-value(<counter-name>) 函数, 它以整数形式返回指定名称计数器的值, 而不是返回字符串。

参见 议题 1026。

附录 A:HTML 的示例样式表

本节为信息性,而非规范性。[HTML] 渲染章节定义了适用于 HTML 列表的规范默认属性;此示例样式表旨在通过熟悉的标记约定来说明 CSS 特性。

关于如何在 CSS 中支持ol[reversed]列表编号的讨论仍在进行中。请参见,例如问题 4181。

/* 设置列表项 */
li {
  display: list-item; /* 隐含 'counter-increment: list-item' */
}

/* 设置 ol 和 ul,使其作用于 list-item 计数器 */
ol, ul {
  counter-reset: list-item;
}

/* 列表的默认样式类型 */
ol { list-style-type: decimal; }
ul { list-style-type: toggle(disc, circle, square); }

/* ol 和 ul 元素上的 type 属性 */
ul[type="disc"]   { list-style-type: disc;   }
ul[type="circle"] { list-style-type: circle; }
ul[type="square"] { list-style-type: square; }
ol[type="1"] { list-style-type: decimal;     }
ol[type="a"] { list-style-type: lower-alpha; }
ol[type="A"] { list-style-type: upper-alpha; }
ol[type="i"] { list-style-type: lower-roman; }
ol[type="I"] { list-style-type: upper-roman; }

/* ol 元素上的 start 属性 */
ol[start] {
  counter-reset: list-item calc(attr(start integer, 1) - 1);
}

/* li 元素上的 value 属性 */
li[value] {
  counter-set: list-item attr(value integer, 1);
}

/* 盒模型规则 */
ol, ul {
  display: block;
  margin-block: 1em;
  marker-side: match-parent;
  padding-inline-start: 40px;
}
ol ol, ol ul, ul ul, ul ol {
  margin-block: 0;
}

li {
  text-align: match-parent;
}

感谢

本规范得益于以下人员的贡献:Aharon Lanin、Arron Eicholz、Brad Kemper、David Baron、Emilio Cobos Álvarez、Mats Palmgren、Oriol Brufau、Simon Sapin、Xidorn Quan。

修改记录

本节记录了自上次发布以来的更改。

自 2020 年 7 月 9 日 WD 以来的更改

自 2019 年 8 月 17 日 WD 以来的更改

自 2019 年 4 月 25 日 WD 以来的更改

自 2014 年 3 月 20 日 WD 以来的更改

与 CSS Level 2 的变化

如引言部分所述, 本模块与 CSS2.1 相比,有显著的变化。

  1. 引入了 ::marker 伪元素,以允许直接样式化列表标记。
  2. list-style-type 现在接受 <string> 以及扩展的 <counter-style> 值,来自 [css-counter-styles-3]。
  3. 引入了 list-item 预定义计数器标识符。
  4. 新增了 counter-set 属性。
  5. 允许使用 inline-level 列表项,如在 [CSS-DISPLAY-3] 中引入的。

合规性

文档约定

合规性要求通过描述性断言和 RFC 2119 术语的组合来表达。关键字“必须”、“必须不”、“要求”、“应”、“不应”、“应该”、“推荐”、“可以”和“可选”在本文件的规范部分应按照 RFC 2119 中的描述进行解释。然而,为了可读性,这些词在本规范中并不都是大写字母。

本规范的所有文本都是规范性的,除了明确标记为非规范性的部分、示例和注释。[RFC2119]

本规范中的示例用“例如”引入,或通过 class="example" 与规范文本分开,如下所示:

这是一个信息性示例。

信息性注释以“注意”开头,并通过 class="note" 与规范文本分开,如下所示:

注意,这是一条信息性注释。

建议是规范性部分,样式上以特殊的方式引起注意,并通过 <strong class="advisement"> 与其他规范文本分开,如下所示: 用户代理必须提供可访问的替代方案。

合规性类别

对本规范的合规性定义有三个合规性类别:

样式表
一个CSS样式表。
渲染器
一个用户代理,它解释样式表的语义并渲染使用它们的文档。
创作工具
一个用户代理,它编写样式表。

如果样式表中使用本模块定义的语法的所有语句在通用 CSS 语法和本模块定义的每个特性的各自语法中都是有效的,那么该样式表就是符合本规范的。

如果渲染器除了根据适当规范解释样式表外,还支持本规范定义的所有特性,通过正确解析这些特性并相应地渲染文档,那么该渲染器就是符合本规范的。然而,用户代理由于设备限制而无法正确渲染文档并不会使用户代理不符合规范。(例如,用户代理不必在单色显示器上渲染颜色。)

如果创作工具编写的样式表根据通用 CSS 语法和本模块中每个特性的各自语法是语法上正确的,并符合本模块中描述的样式表的所有其他合规性要求,则该创作工具就是符合本规范的。

部分实现

为了让作者利用向前兼容的解析规则来分配后备值,CSS 渲染器必须将任何没有可用支持级别的规则、属性、属性值、关键字和其他语法结构视为无效(并适当忽略)。特别是,用户代理必须不选择性地忽略不支持的组件值,并在单个多值属性声明中尊重支持的值:如果任何值被视为无效(因为不支持的值必须如此),CSS 要求整个声明被忽略。

不稳定和专有特性的实现

为了避免与未来稳定的 CSS 特性发生冲突,CSS 工作组建议遵循最佳实践,以实现不稳定特性和专有扩展。

非实验性实现

一旦规范达到候选推荐阶段,非实验性实现是可能的,实施者应发布任何能够证明根据规范正确实现的 CR 级特性的未加前缀实现。

为了建立和维护 CSS 在不同实现中的互操作性,CSS 工作组请求非实验性 CSS 渲染器在发布任何 CSS 特性的未加前缀实现之前,向 W3C 提交实施报告(如有必要,提交用于该实施报告的测试用例)。提交给 W3C 的测试用例将由 CSS 工作组进行审查和更正。

有关提交测试用例和实施报告的更多信息,请访问 CSS 工作组的网站 https://www.w3.org/Style/CSS/Test/。如有问题,请向 public-css-testsuite@w3.org 邮件列表咨询。

索引

本规范定义的术语

通过引用定义的术语

参考文献

规范性参考文献

[CSS-CASCADE-4]
Elika Etemad; Tab Atkins Jr.. CSS 级联与继承 第4版. 2020年8月18日. WD. URL: https://www.w3.org/TR/css-cascade-4/
[CSS-CONTENT-3]
Elika Etemad; Dave Cramer. CSS 生成内容模块 第3版. 2019年8月2日. WD. URL: https://www.w3.org/TR/css-content-3/
[CSS-COUNTER-STYLES-3]
Tab Atkins Jr.. CSS 计数器样式 第3版. 2017年12月14日. CR. URL: https://www.w3.org/TR/css-counter-styles-3/
[CSS-DISPLAY-3]
Tab Atkins Jr.; Elika Etemad. CSS 显示模块 第3版. 2020年5月19日. CR. URL: https://www.w3.org/TR/css-display-3/
[CSS-FLEXBOX-1]
Tab Atkins Jr.; et al. CSS 弹性盒布局模块 第1版. 2018年11月19日. CR. URL: https://www.w3.org/TR/css-flexbox-1/
[CSS-IMAGES-3]
Tab Atkins Jr.; Elika Etemad; Lea Verou. CSS 图像模块 第3版. 2019年10月10日. CR. URL: https://www.w3.org/TR/css-images-3/
[CSS-IMAGES-4]
Tab Atkins Jr.; Elika Etemad; Lea Verou. CSS 图像值与替换内容模块 第4版. 2017年4月13日. WD. URL: https://www.w3.org/TR/css-images-4/
[CSS-OVERFLOW-3]
David Baron; Elika Etemad; Florian Rivoal. CSS 溢出模块 第3版. 2020年6月3日. WD. URL: https://www.w3.org/TR/css-overflow-3/
[CSS-POSITION-3]
Elika Etemad; et al. CSS 定位布局模块 第3版. 2020年5月19日. WD. URL: https://www.w3.org/TR/css-position-3/
[CSS-PSEUDO-4]
Daniel Glazman; Elika Etemad; Alan Stearns. CSS 伪元素模块 第4版. 2019年2月25日. WD. URL: https://www.w3.org/TR/css-pseudo-4/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS 语法模块 第3版. 2019年7月16日. CR. URL: https://www.w3.org/TR/css-syntax-3/
[CSS-TEXT-3]
Elika Etemad; Koji Ishii; Florian Rivoal. CSS 文本模块 第3版. 2020年4月29日. WD. URL: https://www.w3.org/TR/css-text-3/
[CSS-TEXT-4]
Elika Etemad; et al. CSS 文本模块 第4版. 2019年11月13日. WD. URL: https://www.w3.org/TR/css-text-4/
[CSS-VALUES-3]
Tab Atkins Jr.; Elika Etemad. CSS 值和单位模块 第3版. 2019年6月6日. CR. URL: https://www.w3.org/TR/css-values-3/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS 值和单位模块 第4版. 2019年1月31日. WD. URL: https://www.w3.org/TR/css-values-4/
[CSS-VARIABLES-1]
Tab Atkins Jr.. CSS 自定义属性级联变量模块 第1版. 2015年12月3日. CR. URL: https://www.w3.org/TR/css-variables-1/
[CSS-WRITING-MODES-3]
Elika Etemad; Koji Ishii. CSS 书写模式 第3版. 2019年12月10日. REC. URL: https://www.w3.org/TR/css-writing-modes-3/
[CSS-WRITING-MODES-4]
Elika Etemad; Koji Ishii. CSS 书写模式 第4版. 2019年7月30日. CR. URL: https://www.w3.org/TR/css-writing-modes-4/
[CSS2]
Bert Bos; et al. 层叠样式表 第2版 修订版1 (CSS 2.1) 规范. 2011年6月7日. REC. URL: https://www.w3.org/TR/CSS21/
[DOM]
Anne van Kesteren. DOM 标准. 现行标准. URL: https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML 标准. 现行标准. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. 现行标准. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. RFC中指示要求级别的关键字. 1997年3月. 最佳当前实践. URL: https://tools.ietf.org/html/rfc2119
[SELECTORS-4]
Elika Etemad; Tab Atkins Jr.. 选择器 第4版. 2018年11月21日. WD. URL: https://www.w3.org/TR/selectors-4/
[SVG2]
Amelia Bellamy-Royds; et al. 可扩展矢量图形(SVG) 第2版. 2018年10月4日. CR. URL: https://www.w3.org/TR/SVG2/

非规范性参考文献

[CSS-ANIMATIONS-1]
Dean Jackson; et al. CSS 动画 第1版. 2018年10月11日. WD. URL: https://www.w3.org/TR/css-animations-1/
[CSS-COLOR-3]
Tantek Çelik; Chris Lilley; David Baron. CSS 颜色模块 第3版. 2018年6月19日. REC. URL: https://www.w3.org/TR/css-color-3/
[CSS-COLOR-4]
Tab Atkins Jr.; Chris Lilley. CSS 颜色模块 第4版. 2019年11月5日. WD. URL: https://www.w3.org/TR/css-color-4/
[CSS-FONTS-3]
John Daggett; Myles Maxfield; Chris Lilley. CSS 字体模块 第3版. 2018年9月20日. REC. URL: https://www.w3.org/TR/css-fonts-3/
[CSS-TRANSITIONS-1]
David Baron; et al. CSS 过渡. 2018年10月11日. WD. URL: https://www.w3.org/TR/css-transitions-1/

属性索引

名称 值 初始值 适用对象 继承 %ages 动画类型 规范顺序 计算值
counter-increment [ <counter-name> <integer>? ]+ | none none 所有元素 否 不适用 按计算值类型 按语法 关键字 none 或者一个列表,每个项目是一个标识符与一个整数的配对
counter-reset [ <counter-name> <integer>? ]+ | none none 所有元素 否 不适用 按计算值类型 按语法 关键字 none 或者一个列表,每个项目是一个标识符与一个整数的配对
counter-set [ <counter-name> <integer>? ]+ | none none 所有元素 否 不适用 按计算值类型 按语法 关键字 none 或者一个列表,每个项目是一个标识符与一个整数的配对
list-style <'list-style-position'> || <'list-style-image'> || <'list-style-type'> 见各个属性 列表项 见各个属性 见各个属性 见各个属性 按语法 见各个属性
list-style-image <image> | none none 列表项 是 不适用 离散 按语法 关键字 none 或计算出的 <image>
list-style-position inside | outside outside 列表项 是 不适用 离散 按语法 关键字,但见正文
list-style-type <counter-style> | <string> | none disc 列表项 是 不适用 离散 按语法 指定值
marker-side match-self | match-parent match-self 列表项 是 不适用 离散 按语法 指定关键字

问题索引

white-space: pre 的行为不太正确; text-space-collapse: preserve-spaces + text-space-trim: discard-after 可能更接近所需的效果。 查看讨论请参考问题 4448 和 问题 4891。 ↵
这是 CSS2 中的模糊定义,需要一个真实的定义。 ↵
或者,outside 可以将标记布局为主行内框的前一个兄弟。 ↵
为了在标记中正确排序标点,它还需要考虑父元素的direction 属性值。 <https://github.com/w3c/csswg-drafts/issues/4202> ↵
关于重命名关键字和与 list-style-position 合并的问题仍在进行中。 ↵
计数器有时在打印标记以外的地方非常有用。 一般来说,它们提供按顺序编号元素的能力, 这对于其他属性的引用非常有用。 例如,使用order 将一个元素放置在两个特定元素之间 目前要求您在所需插入点之前和/或之后的每个元素上显式设置order。 然而,如果您可以将所有内容的order 值设置为一个计数器, 您可以更轻松地在两个其他元素之间插入一个元素。

其他用例涉及具有稍微不同变换的嵌套或兄弟元素。 如今,您必须使用预处理器以合理的方式做到这一点, 但计数器可以使其在“普通”CSS中有效。

(您可以通过使用自定义属性和堆叠嵌套calc()来构建嵌套情况中的连续值, 但这有点笨拙, 并且对兄弟元素不起作用。)

建议添加一个counter-value(<counter-name>) 函数, 它返回命名计数器的值作为整数, 而不是返回字符串。

请参阅问题 1026。

↵
关于如何在 CSS 中支持ol[reversed] 列表编号的讨论仍在进行中。 参见,例如问题 4181。 ↵