纯JS实现SharePoint动态目录:标题分组逻辑求助
问题背景
需要给包含H1、H2、H3、H4标签的SharePoint维基页面构建动态目录,要求每个H1对应一个<details>容器,内部仅包含该H1下属的H2-H4子标题,实现展开/收起功能。现有代码存在逻辑错误:所有H2-H4会被重复添加到每个H1的<details>中,无法正确按H1分组。
原代码问题分析
原代码分别单独获取所有H1和H2-H4标签,外层遍历H1时,内层直接遍历全部H2-H4并添加到当前<details>,完全忽略了页面中标题的顺序层级关系——每个H1的子标题应该是它之后、下一个H1之前的所有H2-H4。
修正后的代码
function nodesToc() { // 获取页面中所有H1-H4标题,按页面出现顺序排列 const allHeadings = document.querySelectorAll("h1, h2, h3, h4"); let str = ""; let currentDetailsOpen = false; let headingIdCounter = 0; allHeadings.forEach(heading => { const tagName = heading.tagName.toLowerCase(); const classLvl = getClassLvl(tagName.toUpperCase()); // 适配原getClassLvl函数的参数要求 // 给每个标题设置唯一ID const headingId = `title-${headingIdCounter}`; heading.id = headingId; headingIdCounter++; if (tagName === "h1") { // 如果当前有未闭合的details,先闭合 if (currentDetailsOpen) { str += "</details>"; } // 新建H1对应的details容器 str += `<details><summary class='${classLvl}'><a href='#${headingId}'>${heading.innerHTML}</a></summary>`; str += "<ul>"; // 用ul包裹子标题,结构更规范 currentDetailsOpen = true; } else { // H2-H4作为当前H1的子项添加 str += `<li class='${classLvl}'><a href='#${headingId}'>${heading.innerHTML}</a></li>`; } }); // 遍历结束后,闭合最后一个未关闭的details和ul if (currentDetailsOpen) { str += "</ul></details>"; } return str; } // 保留原getClassLvl函数(假设你已有此函数,用于根据标题层级返回样式类) function getClassLvl(tagName) { switch(tagName) { case "H1": return "toc-h1"; case "H2": return "toc-h2"; case "H3": return "toc-h3"; case "H4": return "toc-h4"; default: return ""; } }
修正逻辑说明
- 按顺序获取所有标题:不再分开取H1和子标题,而是一次性获取H1-H4并保持页面原有顺序,确保能正确识别每个H1的下属范围。
- 动态追踪分组状态:用
currentDetailsOpen变量标记是否有未闭合的<details>,遇到新H1时先闭合之前的容器,再创建新的分组。 - 规范子标题结构:用
<ul>包裹每个H1下的子标题,符合HTML列表语义,也便于样式控制。 - 唯一ID生成:用全局计数器生成标题ID,避免原代码中H1和H2-H4 ID重复的问题。
内容的提问来源于stack exchange,提问作者LucienX
相关产品推荐
相关产品推荐

