在 CSS 的世界里,::before 和 ::after 是两位“隐形画家”,而 content 属性就是它们手中的画笔。借助 content,你可以在不触碰 HTML 结构的前提下,为页面添加图标、编号、提示文本、引号甚至图像。它看似简单,却隐藏着许多高级技巧。本文将带你系统掌握 content 的所有用法,并附上可直接上手的代码示例,让你的样式表更加优雅和强大。

一、重新认识 content

1.1 它属于谁?

content 属性 只能 与 ::before 和 ::after 伪元素一起使用(理论上可作用于普通元素,但所有现代浏览器均不支持)。其作用是在元素内部的前后位置插入“生成内容”,这些内容不会出现在 DOM 树中,仅用于视觉呈现。

元素::before {
  content: "插入的内容";
}
1.2 为什么需要它?
  • 减少冗余标签:无需为了加一个图标或分隔线而添加空 <span>。

  • 动态内容:可读取属性值或计数器,实现自动编号。

  • 增强可维护性:将装饰性内容与 HTML 结构分离。

二、基础用法全解

2.1 直接插入字符串

最直观的用法,支持转义序列(如 \A 表示换行):

.important::before {
  content: "⚠️ 注意:";
  color: #d9534f;
}

/* 换行示例 */
.multiline::after {
  content: "\A更多内容";
  white-space: pre;  /* 必须保留空白符 */
}

技巧:使用 white-space: pre; 或 pre-wrap; 可以让 \A 生效。

2.2 引用属性值 attr()

动态获取元素的某个属性值作为内容,最常用于显示链接地址或自定义数据。

/* 显示链接地址 */
a[href]::after {
  content: " (" attr(href) ")";
  font-size: 0.8em;
  color: #6c757d;
}

/* 结合 data-* 属性 */
.tooltip-icon::after {
  content: attr(data-hint);
  /* 样式省略... */
}

attr() 的限制

  • 只能返回 字符串,不能返回颜色、长度等数值(CSS 未来规范可能支持)。

  • 如果属性不存在,会返回空字符串,不会报错。

2.3 插入图像 url()
.icon-external::after {
  content: url("external-link.svg");
  margin-left: 4px;
}

缺点:无法控制图像的宽高(原尺寸显示),也不支持 background-size。 推荐替代方案:使用背景图 + 空内容装饰。

/* 更好的做法 */
.icon-decor::before {
  content: "";
  display: inline-block;
  width: 16px;
  height: 16px;
  background: url("icon.png") no-repeat center / contain;
  margin-right: 4px;
}
2.4 插入引号 open-quote / close-quote

配合 quotes 属性,可以实现自适应不同语言的引号风格:

q {
  quotes: "“" "”" "‘" "’";  /* 外层双引号,内层单引号 */
}
q::before {
  content: open-quote;
}
q::after {
  content: close-quote;
}

<p>鲁迅说:<q>世上本没有路,<q>走的人多了</q>也便成了路</q></p>
<!-- 输出:鲁迅说:“世上本没有路,‘走的人多了’也便成了路” -->
2.5 空内容与纯装饰

最常见的是配合 display、width、height 等属性绘制图形或分割线:

.divider::before {
  content: "";
  display: block;
  height: 1px;
  background: linear-gradient(to right, #ccc, transparent);
  margin: 20px 0;
}
2.6 none 与 normal

两者效果相同,均不生成内容。常用于条件覆盖:

.no-content::before {
  content: none;
}

三、进阶功能:计数器(核心亮点)

计数器是 content 最强大的功能之一,可以实现自动编号、多级目录,甚至替代 <ol> 的复杂样式。

3.1 基础计数器

需要三个属性配合:

  • counter-reset:初始化计数器(通常放在父容器)。

  • counter-increment:递增计数器(放在每个需要编号的子元素上)。

  • content: counter(name):显示当前值。

.section-container {
  counter-reset: section;   /* 初始化名为 section 的计数器 */
}
.section {
  counter-increment: section;  /* 每次递增 1 */
}
.section::before {
  content: counter(section) ". ";
  font-weight: bold;
}
3.2 嵌套计数器(多级编号)

使用 counters() 函数可以显示完整的层级路径,例如 “1.1”、“1.2.1”。

ol {
  counter-reset: item;
  list-style: none;
}
li {
  counter-increment: item;
}
li::before {
  content: counters(item, ".") " ";
}

<ol>
  <li>项目 1
    <ol><li>项目 1.1</li><li>项目 1.2</li></ol>
  </li>
  <li>项目 2</li>
</ol>

输出:

1 项目 1
  1.1 项目 1.1
  1.2 项目 1.2
2 项目 2
3.3 自定义编号格式

counter() 的第二个参数可以指定列表样式(如 upper-roman、lower-alpha):

.chapter::before {
  content: counter(chapter, upper-roman) ". ";
}

四、高级实战技巧

4.1 结合 CSS 自定义属性(变量)
:root {
  --icon-success: "✓";
  --icon-error: "✗";
}
.message.success::before {
  content: var(--icon-success);
  color: #28a745;
}
.message.error::before {
  content: var(--icon-error);
  color: #dc3545;
}
4.2 响应式内容切换

根据屏幕宽度显示不同文字,减少 JavaScript 干预:

.nav-label::before {
  content: "菜单";
}
@media (min-width: 768px) {
  .nav-label::before {
    content: "导航菜单";
  }
}
4.3 纯 CSS 提示框(Tooltip)

利用 attr() + 伪元素 + 悬停状态,实现简洁的提示效果:

<button data-tip="保存成功">提交</button>

[data-tip] {
  position: relative;
}
[data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  bottom: 120%;
  left: 50%;
  transform: translateX(-50%);
  background: #212529;
  color: #fff;
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 0.75rem;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s;
  z-index: 1;
}
[data-tip]:hover::after {
  opacity: 1;
}
4.4 文章章节自动编号

为 h2、h3 自动生成“1 标题”、“1.1 子标题”:

body {
  counter-reset: h2;
}
h2 {
  counter-reset: h3;
  counter-increment: h2;
}
h2::before {
  content: counter(h2) " ";
  color: #007bff;
}
h3 {
  counter-increment: h3;
}
h3::before {
  content: counter(h2) "." counter(h3) " ";
}
4.5 打印时显示链接地址

提升打印文档的可读性:

@media print {
  a[href]::after {
    content: " <" attr(href) ">";
    text-decoration: none;
    color: #000;
  }
}
4.6 面包屑导航分隔符
.breadcrumb li {
  display: inline;
}
.breadcrumb li + li::before {
  content: "/";
  margin: 0 8px;
  color: #6c757d;
}

五、常见误区与注意事项

误区

正确理解

可以在普通元素上用 content

❌ 只有 ::before / ::after 支持

生成的内容会被屏幕阅读器朗读

❌ 辅助技术通常会忽略,重要信息必须放在 HTML 中

url() 插入的图像可以控制尺寸

❌ 原尺寸显示,推荐改用 background-image

attr() 可以返回颜色、长度

❌ 目前只能返回字符串

计数器必须配合 <ol> 使用

❌ 可以用于任何元素,实现任意自动编号

可访问性提示

永远不要依赖 content 来传递关键信息(如表单验证错误、操作指引)。对于装饰性内容则无妨。

性能提示

大量使用 counter 或复杂选择器可能会轻微影响渲染性能,但在正常页面中几乎可以忽略。url() 插入多张图片会增加 HTTP 请求,建议合并或使用图标字体。

六、浏览器兼容性

截至 2026.05.02

  • Chrome、Firefox、Safari、Edge:全功能支持。

  • IE 8+:基础支持(attr() 仅字符串,计数器部分支持;url() 支持)。

  • IE 7 及以下:不支持。

现代项目中可以放心使用,但若需支持 IE 8,注意 counters() 可能存在问题。

总结

content 属性是 CSS 伪元素的灵魂,它用极小的代价实现了动态内容插入、自动编号、属性读取等强大功能。掌握它,你可以:

  • 彻底告别为了装饰而添加的空标签。

  • 轻松实现多级目录、面包屑、自定义列表标记。

  • 创建响应式文本提示和优雅的打印样式。

  • 结合 CSS 变量和数据属性,让样式更加灵活。

但也要牢记:content 生成的只是视觉层的“幻影”,无法替代真实的 HTML 内容,尤其要关注可访问性。在合适的场景下使用它,你的代码会变得更简洁、更语义化。

现在,不妨打开你的项目,尝试用 content 替换掉那些繁琐的装饰性 span 或 i 标签,体验一下“无中生有”的快感吧!

彩蛋

文中配有完整示例,点击《CSS content 属性 · 现代化深色模式完整演示》即可查看。