You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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>

预期显示效果

Firefox截图

解决方案

可以在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);
  });
}

修改说明

  1. 使用querySelectorAll获取所有H1-H3标题,确保按文档顺序遍历,避免原代码只从第一个H1开始遍历的局限性
  2. 定义counters对象跟踪各级标题计数,逻辑和CSS计数器完全一致:
    • 遇到H1时,H1计数加1,重置H2、H3计数
    • 遇到H2时,H2计数加1,重置H3计数
    • 遇到H3时,H3计数加1
  3. 根据标题层级生成对应的编号字符串,将编号与标题内容拼接后设置为目录链接的文本
  4. 保留原有的ID生成和目录项缩进逻辑,确保功能兼容性

修改后生成的目录编号会和CSS自动添加的标题编号完全一致,无需依赖伪元素内容获取。


内容的提问来源于stack exchange,提问作者U. Windl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 20:01:13