好的,我们来梳理一下 CSS 中用于内容生成 (Generated Content) 和 计数器 (Counters) 的主要属性和相关值。这些特性允许你在不修改 HTML 结构的情况下,通过 CSS 向页面添加内容或实现自定义编号。
一、 内容生成 (Generated Content)
内容生成主要通过 ::before 和 ::after 伪元素以及 content 属性来实现。
-
::before 伪元素
- 作用: 在选定元素的内容之前创建一个伪元素。这个伪元素在文档树中不可见,但可以像普通元素一样应用样式。
- 必须与 content 属性配合使用。
- 示例选择器: p::before, .my-class::before
-
::after 伪元素
- 作用: 在选定元素的内容之后创建一个伪元素。
- 必须与 content 属性配合使用。
- 示例选择器: a::after, div#main::after
-
content 属性
-
作用: 定义 ::before 和 ::after 伪元素中要插入的内容。也可以应用于某些替换元素(如
)来提供备用内容(但支持性有限)。
-
值:
- none: (默认值,对 ::before/::after) 不生成任何内容,伪元素不会被渲染。
- normal: (默认值,对非伪元素) 计算为 none。
<string>: 插入一个或多个文本字符串。字符串需要用引号括起来。如 "✓ ", "Important: ".- url(
<url>) : 插入一个外部资源,通常是图片。如 url("icon.png")。 - attr(
<attr-name>) : 插入元素指定属性 (attribute) 的值。如 attr(data-tooltip), attr(href)。 <image>: 除了 url(),还可以是 CSS 渐变<gradient>等。- open-quote / close-quote: 插入合适的引号字符,由 quotes 属性定义。
- no-open-quote / no-close-quote: 不插入引号,但仍然会调整引号嵌套级别。
- counter(
<counter-name>,<counter-style>?) : 插入指定计数器的当前值,可以使用 list-style-type 定义的样式格式化(如 decimal, lower-roman)。第二个参数可选。 - counters(
<counter-name>, <separator-string>, <counter-style>?) : 用于嵌套计数器,插入所有层级计数器的值,并用指定字符串分隔。 - 可以组合多个值: 用空格分隔。如 content: open-quote counter(item) ". " attr(title) close-quote;
-
示例:
a[href^="http"]::after { content: " " url("external-link.svg"); /* 在外部链接后加图标 */ vertical-align: middle; } blockquote::before { content: open-quote; font-size: 2em; } blockquote::after { content: close-quote; font-size: 2em; } li::before { content: counter(step-counter) ": "; /* 显示计数器值 */ font-weight: bold; }
-
-
quotes 属性
-
作用: 定义当 content 属性值为 open-quote 或 close-quote 时,应该使用哪种引号字符。可以定义多层嵌套的引号样式。
-
值:
- none: open-quote 和 close-quote 不产生任何内容。
- auto: (默认值) 浏览器根据内容语言选择合适的引号。
- [
<string> <string>]+: 一个或多个用空格分隔的字符串对。第一对是第一层嵌套引号,第二对是第二层,以此类推。
-
示例:
- quotes: '"' '"' "'" "'"; (外层双引号,内层单引号 - 英文常用)
- quotes: "«" "»" "‹" "›"; (法文或俄文引号)
- quotes: '「' '」' '『' '』'; (日文引号)
-
应用于: 通常应用于需要使用引号的元素,如 blockquote, q。
-
二、 CSS 计数器 (Counters)
CSS 计数器允许你根据元素在文档中的位置来自动编号,常用于创建自定义列表编号、章节标题编号等。
-
counter-reset
-
作用: 创建一个新的计数器或将一个现有计数器重置为指定值。通常应用于父元素或容器元素。
-
值:
- none: (默认值) 不重置任何计数器。
- [
<custom-ident> <integer>?]+: 一个或多个计数器名称 (<custom-ident>) 和可选的初始整数值 (<integer>)。如果省略整数,默认为 0。
-
示例:
- ol { counter-reset: list-item; } (在每个 ol 开始时重置 list-item 计数器为 0)
- body { counter-reset: section 0 heading 1; } (创建/重置 section 为 0,heading 为 1)
- .chapters { counter-reset: chapter; }
-
-
counter-increment
-
作用: 增加一个或多个计数器的值。通常应用于需要计数的元素本身(或其 ::before/::after)。
-
值:
- none: (默认值) 不增加任何计数器。
- [
<custom-ident> <integer>?]+: 一个或多个计数器名称 (<custom-ident>) 和可选的增加步长 (<integer>)。如果省略整数,默认为 1。可以是负数。
-
示例:
- li { counter-increment: list-item; } (每个 li 使 list-item 计数器加 1)
- h2 { counter-increment: section; counter-reset: subsection; } (每个 h2 使 section 加 1,并重置 subsection)
- .task-done { counter-increment: tasks-completed 1; }
-
-
counter-set (较新)
-
作用: 将一个或多个计数器设置为指定的值。与 counter-reset 不同,counter-set 不会创建新的计数器实例,它只是设置当前作用域内计数器的值。如果同一个元素上同时有 counter-reset 和 counter-set,reset 先执行,然后 set 再执行。
-
值:
- none: (默认值) 不设置任何计数器。
- [
<custom-ident> <integer>?]+: 一个或多个计数器名称和要设置的整数值。如果省略整数,默认为 0。
-
示例: li:nth-child(5) { counter-set: list-item 10; } (将第五个列表项的计数器值强制设为 10,后续项会从 11 开始,如果继续使用 counter-increment)
-
-
counter() 函数 (用于 content 属性)
-
作用: 获取指定计数器的当前值,并将其插入到 content 属性中。
-
语法: counter(
<counter-name>, <counter-style>?)<counter-name>: 要获取值的计数器名称。<counter-style>: (可选) 格式化样式,使用 list-style-type 的值,如 decimal (默认), lower-roman, upper-alpha, cjk-ideographic 等。
-
示例:
content: counter(chapter, upper-roman) ". ";
-
-
counters() 函数 (用于 content 属性)
-
作用: 用于嵌套计数器,获取所有层级计数器的值,并用指定字符串连接。
-
语法:
counters(<counter-name>, <separator-string>, <counter-style>?)<counter-name>: 计数器名称。<separator-string>: 用于连接各层级计数器值的字符串。<counter-style>: (可选) 格式化样式。
-
示例: (用于生成类似 "1.1", "1.2", "2.1" 的编号)
ol { counter-reset: item; } li { display: block; counter-increment: item; } li::before { content: counters(item, ".") " "; }
-
总结:
| 属性/伪元素/函数 | 主要作用 | 值/语法 (关键部分) |
|---|---|---|
| ::before | 在元素内容前创建伪元素 | 需配合 content |
| ::after | 在元素内容后创建伪元素 | 需配合 content |
| content | 定义伪元素的内容 | none, , url(), attr(), open-quote, close-quote, counter(), counters() |
| quotes | 定义 open-quote/close-quote 使用的引号字符 | none, auto, [ ]+ |
| counter-reset | 创建或重置计数器 | none, [ ?]+ |
| counter-increment | 增加计数器的值 | none, [ ?]+ |
| counter-set | 设置计数器的值 | none, [ ?]+ |
| counter() | 获取计数器值 (用于 content) | counter([, ]) |
| counters() | 获取嵌套计数器值 (用于 content) | counters(, [, ]) |
案例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS 内容生成与计数器示例</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>CSS 内容生成与计数器演示</h1>
<!-- 示例 1: 使用 ::before 和 ::after 添加简单内容 -->
<section>
<h2><code>::before</code> / <code>::after</code> 与 <code>content</code> (基础)</h2>
<p class="add-prefix-suffix">这段文字前后会通过 CSS 添加内容。</p>
<a href="https://example.com" target="_blank" class="external-link">这是一个外部链接</a>
<p>链接后面会添加一个小图标。</p>
</section>
<!-- 示例 2: 使用 attr() 获取属性值 -->
<section>
<h2>使用 <code>attr()</code> 获取 HTML 属性</h2>
<ul>
<li data-details="这是第一个提示信息">鼠标悬停看 data-details 属性值</li>
<li data-details="这是第二个提示信息">另一个列表项</li>
<li><a href="#section3" title="跳转到计数器部分">链接的 title 属性值会显示在后面</a></li>
</ul>
</section>
<!-- 示例 3: 使用 quotes 和 open-quote/close-quote -->
<section>
<h2>引用样式 (<code>quotes</code>, <code>open-quote</code>, <code>close-quote</code>)</h2>
<blockquote class="styled-quote">
<p>这是第一段引用。"这里是嵌套的引用。" 引用结束。</p>
<p>这是第二段引用。</p>
</blockquote>
<p>上面使用了自定义的引号样式。</p>
</section>
<!-- 示例 4: 基本计数器 (简单列表编号) -->
<section id="section3">
<h2>基本计数器 (<code>counter-reset</code>, <code>counter-increment</code>, <code>counter()</code>)</h2>
<ol class="custom-counter-list">
<li>自定义编号列表项 1</li>
<li>自定义编号列表项 2</li>
<li>自定义编号列表项 3</li>
</ol>
<p>这是一个使用 CSS 计数器自定义编号的有序列表。</p>
</section>
<!-- 示例 5: 嵌套计数器 (章节编号) -->
<section>
<h2>嵌套计数器 (<code>counters()</code>) - 章节编号</h2>
<div class="chapters">
<h1>第一章 介绍</h1>
<h2>背景</h2>
<p>一些介绍内容...</p>
<h2>目标</h2>
<p>一些目标内容...</p>
<h1>第二章 方法</h1>
<h2>数据收集</h2>
<p>关于数据收集...</p>
<h2>数据分析</h2>
<h3>初步分析</h3>
<p>初步分析细节...</p>
<h3>深入分析</h3>
<p>深入分析细节...</p>
<h1>第三章 结论</h1>
<p>总结...</p>
</div>
<p>上面演示了如何使用嵌套计数器生成 "1", "1.1", "2", "2.1", "2.1.1" 这样的编号。</p>
</section>
<!-- 示例 6: 计数器与列表样式结合 -->
<section>
<h2>计数器与列表样式结合</h2>
<ul class="task-list">
<li class="task-done">完成任务 A</li>
<li>进行中任务 B</li>
<li class="task-done">完成任务 C</li>
<li>待办任务 D</li>
<li class="task-done">完成任务 E</li>
</ul>
<p class="task-summary">任务统计信息会显示在这里。</p>
</section>
</body>
</html>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
margin: 20px;
color: #333;
}
h1, h2, h3 {
color: #2c3e50;
margin-bottom: 0.8em;
}
h1 { text-align: center; margin-bottom: 1.5em; }
h2 { border-bottom: 1px solid #bdc3c7; padding-bottom: 0.3em; margin-top: 2em; }
h3 { margin-top: 1.5em; color: #34495e; }
section {
background-color: #ecf0f1;
padding: 15px 20px;
margin-bottom: 30px;
border-radius: 4px;
}
p {
margin-bottom: 1em;
}
ul, ol {
padding-left: 25px; /* 保留一些默认缩进 */
}
li {
margin-bottom: 0.5em;
}
/* --- 示例 1: ::before / ::after 基础 --- */
.add-prefix-suffix::before {
content: "【前缀】"; /* 添加字符串内容 */
color: green;
font-weight: bold;
margin-right: 5px;
}
.add-prefix-suffix::after {
content: " [后缀]";
color: blue;
font-style: italic;
margin-left: 5px;
}
.external-link::after {
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-external-link"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>'); /* 使用 SVG Data URI 图标 */
margin-left: 4px;
vertical-align: middle; /* 图标垂直对齐 */
display: inline-block; /* 确保图标正确显示 */
width: 12px; /* 图标大小 */
height: 12px;
}
/* --- 示例 2: attr() --- */
[data-details]::after { /* 选择所有带 data-details 属性的元素 */
content: " (" attr(data-details) ")"; /* 显示属性值 */
color: #888;
font-size: 0.9em;
display: none; /* 默认隐藏 */
}
[data-details]:hover::after {
display: inline; /* 鼠标悬停时显示 */
}
a[title]::after {
content: " [" attr(title) "]";
font-style: italic;
color: purple;
margin-left: 5px;
}
/* --- 示例 3: quotes --- */
.styled-quote {
/* 外层使用中文引号,内层使用英文双引号 */
quotes: "「" "」" '"' '"';
background-color: #fff;
padding: 15px;
border-left: 5px solid steelblue;
}
.styled-quote p::before {
content: open-quote; /* 使用 quotes 定义的起始引号 */
font-size: 1.5em;
font-weight: bold;
color: steelblue;
margin-right: 0.1em;
vertical-align: -0.3em; /* 微调垂直位置 */
}
.styled-quote p::after {
content: close-quote; /* 使用 quotes 定义的结束引号 */
font-size: 1.5em;
font-weight: bold;
color: steelblue;
margin-left: 0.1em;
vertical-align: -0.3em;
}
/* 处理嵌套引号 - 需要更复杂的选择器,这里简化处理 */
.styled-quote p q::before { content: open-quote; } /* 如果有 q 标签嵌套 */
.styled-quote p q::after { content: close-quote; }
/* --- 示例 4: 基本计数器 --- */
.custom-counter-list {
list-style: none; /* 移除默认的数字标记 */
padding-left: 0;
counter-reset: my-list-counter; /* 创建/重置计数器为 0 */
}
.custom-counter-list li {
counter-increment: my-list-counter; /* 每个 li 使计数器加 1 */
margin-bottom: 8px;
}
.custom-counter-list li::before {
/* 显示计数器值,使用小写罗马数字格式 */
content: counter(my-list-counter, lower-roman) ". ";
color: #c0392b; /* 红色 */
font-weight: bold;
min-width: 30px; /* 确保编号有足够宽度对齐 */
display: inline-block; /* 使得 min-width 生效 */
text-align: right; /* 编号右对齐 */
margin-right: 10px;
}
/* --- 示例 5: 嵌套计数器 --- */
.chapters {
counter-reset: chapter; /* 主章节计数器 */
}
.chapters h1 {
counter-reset: section; /* 重置二级章节计数器 */
counter-increment: chapter; /* 增加主章节计数器 */
margin-left: -20px; /* 稍微调整位置 */
}
.chapters h2 {
counter-reset: subsection; /* 重置三级章节计数器 */
counter-increment: section; /* 增加二级章节计数器 */
margin-left: 0px;
}
.chapters h3 {
counter-increment: subsection; /* 增加三级章节计数器 */
margin-left: 20px;
}
/* 使用 ::before 添加编号 */
.chapters h1::before {
content: counter(chapter) ". ";
color: #16a085;
}
.chapters h2::before {
/* 使用 counters() 显示嵌套编号,用 '.' 连接 */
content: counters(chapter, ".") "." counter(section) " ";
color: #27ae60;
}
.chapters h3::before {
content: counters(chapter, ".") "." counters(section, ".") "." counter(subsection) " ";
color: #2ecc71;
}
/* --- 示例 6: 计数器与列表样式结合 --- */
.task-list {
list-style: none;
padding-left: 0;
counter-reset: total-tasks completed-tasks; /* 初始化两个计数器 */
}
.task-list li {
border-bottom: 1px dotted #ccc;
padding: 8px 0;
counter-increment: total-tasks; /* 每个任务都增加总数 */
}
.task-list li::before {
/* 显示任务总编号 */
content: "#" counter(total-tasks) ": ";
color: gray;
margin-right: 10px;
}
.task-list li.task-done {
text-decoration: line-through; /* 完成的任务加删除线 */
color: #95a5a6;
counter-increment: completed-tasks; /* 完成的任务增加完成计数 */
}
.task-list li.task-done::after {
content: " ✓"; /* 完成的任务加勾号 */
color: green;
font-weight: bold;
}
.task-summary::before {
/* 使用两个计数器显示统计信息 */
content: "已完成 " counter(completed-tasks) " / " counter(total-tasks) " 项任务。";
display: block; /* 让统计信息独占一行 */
font-weight: bold;
color: steelblue;
margin-top: 15px;
border-top: 1px solid #ccc;
padding-top: 10px;
}