code标签无法直接用css计数器实现行号,因文本为连续流;有效方案是用pre+ol+li结构或highlightjs-line-numbers.js插件,兼顾可访问性、复制纯净与语义正确。

code 标签本身不支持行号,强行往 code 上加 ::before 计数器或 JS 插入数字,只会让复制粘贴失效、换行错位、缩进丢失——这是绝大多数人踩坑的起点。
为什么不能直接对 code 元素用 CSS counter 实现行号
浏览器把 code 当作行内元素,即使设了 display: block,它的文本内容仍是连续流,::before 只会触发一次;想按“物理行”编号,必须让每行成为可独立定位的块级容器。常见错误写法:
pre code {
counter-increment: line;
}
pre code::before {
content: counter(line) ". ";
}
结果是所有行前面都显示 “1.”。真正有效的做法是:把每行内容包裹成单独的 span 或 div,再在这些元素上递增计数器。
用 pre + ol + li 实现可复制、保格式、带行号的方案
这是目前唯一能同时满足「保留空格/制表符」「复制无行号」「换行不乱序」「纯 CSS 驱动」的方案。关键点在于放弃 code 的语义封装,改用列表结构模拟代码块:
-
ol设置counter-reset: line,li设置counter-increment: line - 每个
li内用white-space: pre(不是pre-wrap)保持原始缩进和换行 -
li::before插入行号,并用float: left或inline-block + width控制右对齐 - 复制时浏览器默认忽略
::before伪元素内容,所以粘贴出来只有纯代码
示例 HTML:
<ol class="code-list">
<li>function hello() {</li>
<li> console.log("world");</li>
<li>}</li>
</ol>
用 highlight.js + highlightjs-line-numbers.js 快速启用行号(推荐用于博客类站点)
如果你已用 highlight.js 做语法高亮,别自己手写计数逻辑。官方生态有成熟插件:highlightjs-line-numbers.js,它通过 DOM 操作在 pre 内插入绝对定位的行号列,不影响 code 内容结构:
- 必须在
hljs.highlightAll()之后调用hljs.initLineNumbersOnLoad() - 插件会自动识别
pre code,但要求code是pre的**直接子元素**,嵌套层级多会失效 - 行号列默认宽度固定(约 2.5em),若代码行数超 3 位数,需手动加 CSS 调整
.hljs-ln-numbers的min-width - 复制时仍会带上行号——这是该插件的固有限制,如需纯净复制,得配合 JS 剥离逻辑或换用
ol方案
引入顺序不能错:
<link rel="stylesheet" href="highlight.min.css"><script src="highlight.min.js"></script><script src="highlightjs-line-numbers.min.js"></script><script> hljs.highlightAll(); hljs.initLineNumbersOnLoad(); </script>
行号对可访问性和 SEO 的隐性影响
行号本质是装饰性内容,不该出现在 DOM 主干中干扰屏幕阅读器或搜索引擎解析。最容易被忽略的一点是:ol 方案会让辅助技术把代码误读为“有序列表”,而 pre code 方案配合 ARIA 属性更稳妥:
- 给
pre加role="region"和aria-label="代码示例" - 给行号容器加
aria-hidden="true"(如.pre-numbering) - 避免用
ol/li包裹真实代码,除非你明确接受其语义变更
真正的难点不在“怎么加”,而在“加完之后,用户还能不能像没加一样用这段代码”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











