如何通过CSS/JS将标题:before伪元素编号同步到生成的目录?
问题描述
我有一个包含H1至H3标题的HTML页面,已通过CSS规则自动为标题添加层级编号,效果正常。同时我用JavaScript编写了一段代码,提取页面标题的textContent生成目录并填充到空的span元素中,这部分功能也能正常运行,但目录里缺少CSS自动添加的编号。
我尝试过用JavaScript获取元素:before伪元素的内容,但得到的是样式规则代码(例如H3元素对应的是counter(c_h1) "." counter(c_h2) "." counter(c_h3) " "),而非实际渲染出的编号值(例如1.3.1 ),所以这个方法对我没用。请问有没有办法让JavaScript生成的目录拥有与CSS规则自动添加的一致的标题编号?
示例代码
CSS 样式
.pp { font-family: sans-serif } h1, h2, h3 { font-family: sans-serif; page-break-after: avoid; break-after: avoid } /* counters */ :root { counter-reset: c_h1 0 c_h2 0 c_h3 0 c_h4 0 } h1:before { counter-reset: c_h2 0 c_h3 0 c_h4 0; counter-increment: c_h1; content: counter(c_h1)" " } h2:before { counter-reset: c_h3 0 c_h4 0; counter-increment: c_h2; content: counter(c_h1)"."counter(c_h2)" " } h3:before { counter-reset: c_h4 0; counter-increment: c_h3; content: counter(c_h1)"."counter(c_h2)"."counter(c_h3)" " } /* table of contents */ span.toc:not(:empty) { border-style: solid; border-width: thin; border-color: black; padding: 1ex; margin-top: 1em; margin-bottom: 1em; display: inline-block } div.toc-title { font-family: sans-serif; font-weight: bold; padding-bottom: 1ex } div.toc-H1 { padding-left: 1em } div.toc-H2 { padding-left: 2em } div.toc-H3 { padding-left: 3em }
HTML 及原JavaScript代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="de-DE" xml:lang="de-DE"> <head> <title>Example</title> <script> "use strict"; // Create table of contents from headings function make_TOC(target_ID) { const candidate = /^H[1-9]$/i; { let list = document.getElementsByTagName("H1"); let toc = document.getElementById(target_ID); let seq = 0; var e; if (list.length > 0) { e = list.item(0); let div = document.createElement("div"); div.setAttribute("class", "toc-title"); div.textContent = "Inhaltsverzeichnis"; toc.appendChild(div); } while (e !== null) { let n = e.tagName; if (n.search(candidate) !== -1) { let div = document.createElement("div"); let link = document.createElement("a"); let id = e.id; div.setAttribute("class", "toc-" + n); div.appendChild(link); link.textContent = e.textContent; if (id === "") { // add id id = "H." + ++seq; e.id = id; } link.setAttribute("href", "#" + id); toc.appendChild(div); } e = e.nextElementSibling; } } } </script> </head> <body> <span class="toc" id="toc"></span> <h1 class="pp">H1: D...</h1> <h2 class="pp">H2: Z...</h2> <p class="pp">D...</p> <ul class="pp"> <li class="pp">B...</li> <li class="pp">U...</li> </ul> <p class="pp">D...</p> <h2 class="pp">H2: C...</h2> <p class="pp">D...</p> <p class="pp">A...</p> <h2 class="pp">H2: E...</h2> <h3 class="pp">H3: S...</h3> <p class="pp">U...</p> <h3 class="pp">H3: R...</h3> <p class="pp">S...</p> <h3 class="pp">H3: L...</h3> <p class="pp">S...</p> <h3 class="pp">H3: W...</h3> <p class="pp">N...</p> <script type="text/javascript"> make_TOC("toc") </script> </body> </html>
预期显示效果

解决方案
可以在JavaScript中模拟CSS计数器的逻辑,跟踪各级标题的计数,生成对应的编号后添加到目录项中。修改后的make_TOC函数如下:
"use strict"; // Create table of contents from headings function make_TOC(target_ID) { const candidate = /^H[1-9]$/i; let toc = document.getElementById(target_ID); let seq = 0; // 模拟CSS计数器逻辑 let counters = { h1: 0, h2: 0, h3: 0 }; // 获取文档中所有H1-H3标题,按顺序遍历 let headings = document.querySelectorAll('h1, h2, h3'); if (headings.length > 0) { let div = document.createElement("div"); div.setAttribute("class", "toc-title"); div.textContent = "Inhaltsverzeichnis"; toc.appendChild(div); } headings.forEach(e => { let tagName = e.tagName.toLowerCase(); let id = e.id; // 根据标题层级更新计数器,和CSS规则完全同步 if (tagName === 'h1') { counters.h1++; counters.h2 = 0; counters.h3 = 0; } else if (tagName === 'h2') { counters.h2++; counters.h3 = 0; } else if (tagName === 'h3') { counters.h3++; } // 生成对应层级的编号字符串 let number = ''; switch(tagName) { case 'h1': number = `${counters.h1} `; break; case 'h2': number = `${counters.h1}.${counters.h2} `; break; case 'h3': number = `${counters.h1}.${counters.h2}.${counters.h3} `; break; } // 创建目录项并添加编号 let div = document.createElement("div"); let link = document.createElement("a"); div.setAttribute("class", `toc-${e.tagName}`); link.textContent = `${number}${e.textContent}`; // 为无ID的标题生成ID if (id === "") { id = "H." + ++seq; e.id = id; } link.setAttribute("href", "#" + id); div.appendChild(link); toc.appendChild(div); }); }
修改说明
- 使用
querySelectorAll获取所有H1-H3标题,确保按文档顺序遍历,避免原代码只从第一个H1开始遍历的局限性 - 定义
counters对象跟踪各级标题计数,逻辑和CSS计数器完全一致:- 遇到H1时,H1计数加1,重置H2、H3计数
- 遇到H2时,H2计数加1,重置H3计数
- 遇到H3时,H3计数加1
- 根据标题层级生成对应的编号字符串,将编号与标题内容拼接后设置为目录链接的文本
- 保留原有的ID生成和目录项缩进逻辑,确保功能兼容性
修改后生成的目录编号会和CSS自动添加的标题编号完全一致,无需依赖伪元素内容获取。
内容的提问来源于stack exchange,提问作者U. Windl
相关产品推荐
相关产品推荐

