码迷,mamicode.com
首页 > 编程语言 > 详细

JavaScript 遍历文档生成目录结构

时间:2021-06-02 15:54:18      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:文章   date   清除   sar   第一部分   tac   特殊情况   代码   alt   

一、需求描述

在 Word 中编辑文档的时候,可以在视图中打开导航窗格来查看目录树

类似的,现在需要基于页面上的文章,渲染出一个这样的目录结构

技术图片 

在网页上这些标题都是通过 <h1> 这样的标签渲染的,而且段落与标题之间是兄弟节点的关系

所以第一步只需要获取到文章的根节点,然后遍历 <h1> 这样的兄弟节点,就能拿到初步的目录结构

 

但有一种特殊情况需要考虑:

可能文章中的第一个标题并不是 h1,而是更低层级的标题,比如 h3,但在显示上依然需要作为一级标题来展示,因为在 h3 之前没有更大的标题

同样的,在 h1 下面如果先出现了 h3,紧接着又出现了 h2,那么先出现的 h3 实际上和后面的 h2 处于一个层级

也就是说类似这样的结构:

<h3>标题3</h3>
<h4>标题4</h4>
<h1>标题1</h1>
<h2>标题2</h2>
<h1>标题1</h1>
<h4>标题4</h4>
<h3>标题3</h3>
<h2>标题2</h2>

需要展示为:

技术图片

 

 

二、程序设计

虽然页面上的文章是一棵 DOM 树,但由于标题元素是块级元素,所以实际上需要处理的树节点是平铺的,只有一个层级

也就是说,不管是怎样的文档,最终都能处理成这样的结构:

const article = [
  { tag: h3,content: 标题3 },
  { tag: p, content: 这里是第一部分的内容 },
  { tag: h4, content: 标题4 },
  { tag: p, content: 这里是第二部分的内容 },
  { tag: p, content: 上面说得很好,接下来再补充一点 },
  { tag: h1, content: 标题1 },
  { tag: h2, content: 标题2 },
  { tag: h1, content: 标题1 },
  { tag: p, content: 刚才有一点忘记说了 },
  { tag: p, content: 我话讲完,谁赞成,谁反对 },
  { tag: h4, content: 标题4 },
  { tag: h3, content: 标题3 },
  { tag: p, content: 不好意思,你刚才说什么我没听清 },
  { tag: h2, content: 标题2 },
  { tag: p, content: 现在我再问一次,谁赞成,谁反对 },
]

所以对于文档本身,只需要做一次遍历即可

但是对于文档目录,由于最终计算的是一个相对层级,所以也不太方便使用固定长度的数组来记录层级

所以最终的解决方案是维护一个栈来记录标题的层级关系

在一开始的时候,对于标题节点无论是几级标题,都直接压栈

后面每次处理标题,都和栈尾的标题进行比较,如果当前的标题层级更深,则压入栈内,否则清除栈尾,并比较前一位标题

 

在处理标题层级的同时,还需要另外维护一个记录前缀的栈,这两个栈是映射关系

最终可以通过这两个栈,得到目录的完整文案,甚至是缩进量,所以出参可以这样的结构:

const result = [
  { title: 1 标题, indent: 0 },
  { title: 1.1 标题, indent: 1 },
]

 

 

三、代码实现

function getHeadingList(list) {
    if (!Array.isArray(list)) {
        return;
    }

    const reg = /h(\d)/; // 使用正则来匹配标题节点
    const levelStack = []; // 记录标题层级
    const prefixStack = []; // 记录前缀

    return list.reduce((res, node) => {
        const { tag, content } = node || {};
        const tagSplited = reg.exec(tag);
        if (!tagSplited) return res;

        updateLevelList(levelStack, prefixStack, Number(tagSplited[1]));

        res.push({
            title: `${prefixStack.join(".")} ${content}`,
            indent: prefixStack.length - 1,
        });
        return res;
    }, []);
}

function updateLevelList(levelStack, prefixStack, current) {
    const idx = levelStack.length - 1;
    const lastLevel = levelStack[idx];
    if (!lastLevel || current > lastLevel) {
        // 当前为最深层级,压入栈尾
        levelStack.push(current);
        prefixStack.push(1);
        return;
    }

    if (current === lastLevel) {
        // 层级相等时,只修改前缀
        prefixStack[idx]++;
    } else if (current < lastLevel) {
        // 当前层级更高,先和上一层级对比
        const preIndex = idx - 1;
        const preLevel = levelStack[preIndex];
        if (current > preLevel) {
            // 如果上一层级比当前层级更高,即 [1, 3, 2] 这种情况
            prefixStack[idx]++;
            levelStack[idx] = current;
        } else {
       // 删除栈尾,继续递归 levelStack.splice(idx,
1); prefixStack.splice(idx, 1); updateLevelList(levelStack, prefixStack, current); } } }

 

JavaScript 遍历文档生成目录结构

标签:文章   date   清除   sar   第一部分   tac   特殊情况   代码   alt   

原文地址:https://www.cnblogs.com/wisewrong/p/14825769.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!