CSS 内容生成与计算器 15

128 阅读10分钟

好的,我们来梳理一下 CSS 中用于内容生成 (Generated Content)计数器 (Counters) 的主要属性和相关值。这些特性允许你在不修改 HTML 结构的情况下,通过 CSS 向页面添加内容或实现自定义编号。


一、 内容生成 (Generated Content)

内容生成主要通过 ::before 和 ::after 伪元素以及 content 属性来实现。

  1. ::before 伪元素

    • 作用: 在选定元素的内容之前创建一个伪元素。这个伪元素在文档树中不可见,但可以像普通元素一样应用样式。
    • 必须与 content 属性配合使用。
    • 示例选择器: p::before, .my-class::before
  2. ::after 伪元素

    • 作用: 在选定元素的内容之后创建一个伪元素。
    • 必须与 content 属性配合使用。
    • 示例选择器: a::after, div#main::after
  3. 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;
      }
          
      
  4. quotes 属性

    • 作用: 定义当 content 属性值为 open-quote 或 close-quote 时,应该使用哪种引号字符。可以定义多层嵌套的引号样式。

    • 值:

      • none: open-quote 和 close-quote 不产生任何内容。
      • auto: (默认值) 浏览器根据内容语言选择合适的引号。
      • [<string> <string>]+: 一个或多个用空格分隔的字符串对。第一对是第一层嵌套引号,第二对是第二层,以此类推。
    • 示例:

      • quotes: '"' '"' "'" "'"; (外层双引号,内层单引号 - 英文常用)
      • quotes: "«" "»" "‹" "›"; (法文或俄文引号)
      • quotes: '「' '」' '『' '』'; (日文引号)
    • 应用于: 通常应用于需要使用引号的元素,如 blockquote, q。


二、 CSS 计数器 (Counters)

CSS 计数器允许你根据元素在文档中的位置来自动编号,常用于创建自定义列表编号、章节标题编号等。

  1. 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; }
  2. 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; }
  3. 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)

  4. 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) ". ";

  5. 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;
}