解析html内容的h标签成目录树(markdown解析出来的html)

news2024/10/6 6:44:43

一.本人用的markdown插件是cherry-markdown,个人觉得比较好用,画图和数学公式都整合的很好

https://github.com/Tencent/cherry-markdown

二.背景

     经过markdown解析的html,要取里面的h标签转换成目录树,发现这里面都要人工计算,没有发现有插件可以解析

三.效果图

四.js原生代码方法

1.方法一:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<style>
    .a-menu {
        height: calc(100vh - 334px);
        overflow: hidden;
        padding-right: 8px;
        padding-top: 8px;
        padding-bottom: 8px;
        background: rgba(251, 251, 251, 0);
        transition: ease-in-out 0.16s;
    }
    @media screen and (max-width: 640px) {
        .a-menu {
            height: calc(4vh);
        }
    }
    .a-menu::-webkit-scrollbar {
        width: 7px;
    }
    .a-menu::-webkit-scrollbar-thumb {
        background: #d9dde6;
        transition: ease-in-out 0.32s;
    }
    .a-menu::-webkit-scrollbar-thumb:hover {
        background: #cbd0db;
        transition: ease-in-out 0.32s;
    }
    .a-menu:hover {
        overflow-y: auto;
        transition: ease-in-out 0.16s;
    }
    .a-menu .a-list {
        display: flex;
        flex-direction: column;
        padding-top: 4px;
        box-sizing: border-box;
    }
    .a-menu .a-list.is-child .a-list-content a {
        padding: 2px 0 2px 16px;
        font-size: 12px;
    }
    .a-menu .a-list .a-list-content {
        display: inline-flex;
        flex-direction: column;
        margin-left: -2px;
        font-size: 14px;
        color: #666666;
    }
    .a-menu .a-list .a-list-content .a-list .a-list-content {
        display: block;
        position: relative;
    }
    .a-menu .a-list .a-list-content .a-list .a-list-content a {
        padding-right: 0;
    }
    .a-menu .a-list .a-list-content .a-list .a-list-content.colorActive::after {
        background-color: #248bff;
    }
    .a-menu .a-list .a-list-content .a-list .a-list-content::after {
        content: '';
        display: inline-block;
        position: absolute;
        left: 4px;
        top: 0;
        bottom: 0;
        width: 1px;
        background-color: #dfdfdf;
    }
    .a-menu .a-list .a-list-content a {
        display: inline-flex;
        align-items: center;
        margin: 4px 0;
        padding-right: 20px;
        position: relative;
        color: #999;
        overflow: hidden;
    }
    .a-menu .a-list .a-list-content a .space-inner {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .a-menu .a-list .a-list-content a:hover {
        color: #165dff;
    }
    .a-menu .a-list .a-list-content a:hover.has-children::before {
        border-bottom-color: #248bff;
        border-right-color: #248bff;
    }
    .a-menu .a-list .a-list-content a.has-children {
        display: flex;
        color: #000;
    }
    .a-menu .a-list .a-list-content a.has-children::before {
        content: '';
        display: flex;
        width: 6px;
        height: 6px;
        position: absolute;
        right: 0px;
        top: 2px;
        transform: rotate(45deg);
        border-bottom: 1px solid #C9CDD4;
        border-right: 1px solid #C9CDD4;
    }
    .a-menu .a-list .a-list-content a.is-open::before {
        top: 6px;
        transform: rotate(-135deg);
    }
    .a-menu .a-list .a-list-content a:not(.is-open) + .a-list {
        display: none;
    }
    .a-menu .a-list .a-list-content a.activeLink {
        color: #248bff;
    }
    .a-menu > .a-list > .a-list-content > a {
        font-size: 14px;
        color: #000;
    }

</style>
<body>
<div class="a-menu" id="menuBox">
</div>


<script>


    let contentHtmlStr = `<div data-inline-code-theme="red" data-code-block-theme="tomorrow night"><h1 data-lines="1" data-sign="70af8ccd3da1fa06b19ac1bbab643567" id="一级目录"><a href="#一级目录" class="anchor"></a><strong>一级目录</strong></h1><h2 data-lines="1" data-sign="b961c3193476d88b1d341e95b5d80327" id="二级目录"><a href="#二级目录" class="anchor"></a><strong>二级目录</strong></h2><h1 data-lines="1" data-sign="25aae3b2a2128daa60fd958f2bcd3d2d" id="一级目录-3"><a href="#一级目录-3" class="anchor"></a><strong>一级目录</strong></h1><h3 data-lines="1" data-sign="08fddbf5db1e7127bc127ee6d4727500" id="三级目录"><a href="#三级目录" class="anchor"></a><strong>三级目录</strong></h3><h1 data-lines="1" data-sign="e7b867ff368ac6b72c919ed3512bad68" id="一级目录-4"><a href="#一级目录-4" class="anchor"></a><strong>一级目录</strong></h1><h2 data-lines="1" data-sign="5e46a853fffe62d8533d1ec70c30d75e" id="二级目录-3"><a href="#二级目录-3" class="anchor"></a><strong>二级目录</strong></h2><h3 data-lines="1" data-sign="35c88e5d8a4fe471473ff3399fb80d67" id="三级目录-3"><a href="#三级目录-3" class="anchor"></a><strong>三级目录</strong></h3><h2 data-lines="1" data-sign="e84f6131c5fa7a71ccce5ede7f6d4f44" id="二级目录-4"><a href="#二级目录-4" class="anchor"></a><strong>二级目录</strong></h2><h3 data-lines="1" data-sign="42140303320892247936dbddd25f6437" id="三级目录-4"><a href="#三级目录-4" class="anchor"></a><strong>三级目录</strong></h3><h2 data-lines="1" data-sign="6841f56875dca1f8202c2d107728d51e" id="二级目录-5"><a href="#二级目录-5" class="anchor"></a><strong>二级目录</strong></h2><h3 data-lines="1" data-sign="e2e87e66fe9a4d690d5b39295f68db9b" id="三级目录-5"><a href="#三级目录-5" class="anchor"></a><strong>三级目录</strong></h3><h4 data-lines="1" data-sign="60fc5695a612f0345a887c03e2270fc2" id="四级目录"><a href="#四级目录" class="anchor"></a><strong>四级目录</strong></h4><h3 data-lines="1" data-sign="44539014907c9a6576c85d3f680f7451" id="三级目录-6"><a href="#三级目录-6" class="anchor"></a><strong>三级目录</strong></h3><h3 data-lines="1" data-sign="b46bc9232cf0159857bf60b3aa2c5195" id="三级目录-7"><a href="#三级目录-7" class="anchor"></a><strong>三级目录</strong></h3><h1 data-lines="1" data-sign="d71970fb2368a14fc5aa82e29129173c" id="一级目录-5"><a href="#一级目录-5" class="anchor"></a><strong>一级目录</strong></h1><h4 data-lines="1" data-sign="f9812f5521a2221003e49755afcc29c6" id="四级目录-3"><a href="#四级目录-3" class="anchor"></a><strong>四级目录</strong></h4><h1 data-lines="1" data-sign="e46816d23a866b8523bf08e1d391ab7d" id="一级目录-6"><a href="#一级目录-6" class="anchor"></a><strong>一级目录</strong></h1><h3 data-lines="1" data-sign="67fbe72a8d079f663ccbda69ee668da0" id="三级目录-8"><a href="#三级目录-8" class="anchor"></a><strong>三级目录</strong></h3><h1 data-lines="1" data-sign="3c5a1ea69b73db6836bfbf3bb2224c26" id="一级目录-7"><a href="#一级目录-7" class="anchor"></a><strong>一级目录</strong></h1><h2 data-lines="1" data-sign="3fc89b827eb2b5a4d54859e843cff7d5" id="二级目录-6"><a href="#二级目录-6" class="anchor"></a><strong>二级目录</strong></h2><h1 data-lines="1" data-sign="5dee0f6d3936fc8911a76a904223b3d9" id="一级目录-8"><a href="#一级目录-8" class="anchor"></a><strong>一级目录</strong></h1><h4 data-lines="1" data-sign="13b7a1aecb892fe01daf9d307832d5c0" id="四级目录-4"><a href="#四级目录-4" class="anchor"></a><strong>四级目录</strong></h4><h1 data-lines="1" data-sign="d1dbf5c2c258c99a96adc878965ef1f0" id="一级目录-9"><a href="#一级目录-9" class="anchor"></a><strong>一级目录</strong></h1><h3 data-lines="1" data-sign="a67216f009b8c8d794a9b7fbfab117ce" id="三级目录-9"><a href="#三级目录-9" class="anchor"></a><strong>三级目录</strong></h3><h1 data-lines="1" data-sign="0eaafae5a4e4f62a672c9829163a936b" id="一级目录-10"><a href="#一级目录-10" class="anchor"></a><strong>一级目录</strong></h1><h2 data-lines="1" data-sign="56a41f5ab1aa716f10386e3198e1894d" id="二级目录-7"><a href="#二级目录-7" class="anchor"></a><strong>二级目录</strong></h2><h3 data-lines="1" data-sign="15462102af967ea2a4d18f7cdab278e0" id="三级目录-10"><a href="#三级目录-10" class="anchor"></a><strong>三级目录</strong></h3><h4 data-lines="1" data-sign="d20063c4955759c16f40a54a6fe0e223" id="四级目录-5"><a href="#四级目录-5" class="anchor"></a><strong>四级目录</strong></h4><h5 data-lines="1" data-sign="4f474aa0317f5fab32746ed19e8b0a08" id="五级目录"><a href="#五级目录" class="anchor"></a><strong>五级目录</strong></h5><h6 data-lines="1" data-sign="e54b3244c958b60083fd0fd1f0a79448" id="六级目录"><a href="#六级目录" class="anchor"></a><strong>六级目录</strong></h6><h1 data-lines="1" data-sign="b0d40797eed445eb5e29a011e7fe1e1f" id="一级目录-11"><a href="#一级目录-11" class="anchor"></a><strong>一级目录</strong></h1><h2 data-lines="1" data-sign="f055a5ed87cfe7934262638b5ea1e076" id="二级目录-8"><a href="#二级目录-8" class="anchor"></a><strong>二级目录</strong></h2><h3 data-lines="1" data-sign="5ed4b1b9e11053095450d803a66e8595" id="三级目录-11"><a href="#三级目录-11" class="anchor"></a><strong>三级目录</strong></h3><h4 data-lines="1" data-sign="6ac151d6f6d0a08da7eda2ff1b644280" id="四级目录-6"><a href="#四级目录-6" class="anchor"></a><strong>四级目录</strong></h4><h5 data-lines="1" data-sign="de2883023dc43dda752c05db796f8739" id="五级目录-3"><a href="#五级目录-3" class="anchor"></a><strong>五级目录</strong></h5><h6 data-lines="1" data-sign="71084d01bb4fa484cab4b75b7d531bd2" id="六级目录-3"><a href="#六级目录-3" class="anchor"></a><strong>六级目录</strong></h6></div>`

    //获取当前dom应该向左边移动的位置,因为是目录树,移动有继承关系,这边需要计算
    function getItemPaddingLeft(item){
        let defaultVal = 16;
        let maxTreeLevel =1;
        //目录层级等于1是最大层级,上面没有父类
        if(item.treeLevel == 1){
            return defaultVal+'px';
        }
        //当前不是目录树最大层级,上面没有父类
        if(item.pid == 0){
            return (defaultVal+16*(item.treeLevel-maxTreeLevel))+'px';
        }
        //剩下的就是有父类,不是最大层级(这一类不用加默认距离,因为父类已经加,会继承)
        return (16*(item.treeLevel-item.parentTreeLevel))+'px';
    }
    function getContentTree(contentHtmlStr) {
        if(!contentHtmlStr) {
            return [];
        }
        let hDomList = contentHtmlStr.match(
            /<h[1-6]{1}[^>]*>([\s\S]*?)<\/h[1-6]{1}>/g
        );
        if(!Array.isArray(hDomList)){
            return [];
        }
        const dirTree = [];
        //最大的h标签值
        let maxhLevel;
        hDomList.forEach((hDom,index)=>{
            const startIndex = hDom.indexOf("id=\"");
            const endIndex = hDom.indexOf("\">");
            if(startIndex!=-1 && endIndex!=-1){
                const re = /<h([1-6]{1})[^>]*>([\s\S]*?)<\/h[1-6]{1}>/
                let hLevel  = Number(hDom.replace(re, "$1"));
                const domId = hDom.slice(startIndex+4,endIndex);
                let titleContent = hDom.replace(re, "$2");
                // 有可能匹配到的是这样’我<strong>是</strong>h1<em>斜体</em>介绍‘ 里面还有标签(直接去掉标签)
                let title = titleContent.replaceAll(/<([\s\S]*?)>|<\/([\s\S]*?)>/g, "");
                const currentDir = {
                    id:(index+1),//用于点击的时候选中态
                    title,
                    domId,
                    hLevel,//h标签值(1,2,3,4,5,6)
                    isOpen:true
                }
                if(dirTree.length>0){
                    //查找最近的父类(将数组反转才能找到最近的)
                    //reverse会改变原数组,JSON.parse(JSON.stringify())方法去一下生成新数组
                    let reverseDirTree = JSON.parse(JSON.stringify(dirTree)).reverse();
                    let findParent = reverseDirTree.find(i=>currentDir.hLevel > i.hLevel);
                    if(findParent){
                        currentDir.pid = findParent.id;
                    }else{
                        currentDir.pid = 0;
                    }
                }else{
                    currentDir.pid = 0;
                }
                if(maxhLevel){
                    if(currentDir.hLevel < maxhLevel){
                        maxhLevel = currentDir.hLevel;
                    }
                }else{
                    maxhLevel = currentDir.hLevel;
                }
                dirTree.push(currentDir)
            }
        })
        dirTree.forEach((item)=>{
            item.parentTreeLevel = 0;//父类默认是0
            item.treeLevel = (item.hLevel - maxhLevel) +1;//目录树层级,也代表当前h标签解析的内容在父类属于第几层级,1是最大层级
            if(item.pid!==0){
                const findParent = dirTree.find(i=>i.id === item.pid);
                if(findParent){
                    if(!Array.isArray(findParent.children)){
                        findParent.children = []
                    }
                    item.parentTreeLevel = (findParent.hLevel - maxhLevel) +1;//父目录树层级,也代表当前h标签解析的内容在父类属于第几层级,1是最大层级
                    findParent.children.push(item)
                }
            }
        })
        return dirTree.filter((i)=>i.pid === 0);
    }

    let contentTree = getContentTree(contentHtmlStr);

    let contentTreeHtml = '';

    function calcContentTreeHtml(contentTree) {
        if (contentTree.length > 0) {
            contentTreeHtml +=`<div class="a-list">`
            for (let i in contentTree) {
                contentTreeHtml +=`
                      <div
                          style="padding-left:${getItemPaddingLeft(contentTree[i])}"
                          class="a-list-content"
                      >
                        <a class="${(Array.isArray(contentTree[i].children) && contentTree[i].children.length>0 && contentTree[i].isOpen)?'is-open':''} ${(Array.isArray(contentTree[i].children) && contentTree[i].children.length>0)?'has-children':''}">
                          <span class="space-inner" title="${contentTree[i].title}">${contentTree[i].title}</span>
                        </a>
                      `
                if (Array.isArray(contentTree[i].children) && contentTree[i].children.length>0) {
                    calcContentTreeHtml(contentTree[i].children);
                }
                contentTreeHtml += `</div>`;
            }
            contentTreeHtml += `</div>`;
        }
    }

    calcContentTreeHtml(contentTree);

    document.getElementById('menuBox').innerHTML = contentTreeHtml;

</script>


</body>
</html>

2.方法二:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<style>
    .a-menu {
        height: calc(100vh - 334px);
        overflow: hidden;
        padding-right: 8px;
        padding-top: 8px;
        padding-bottom: 8px;
        background: rgba(251, 251, 251, 0);
        transition: ease-in-out 0.16s;
    }
    @media screen and (max-width: 640px) {
        .a-menu {
            height: calc(4vh);
        }
    }
    .a-menu::-webkit-scrollbar {
        width: 7px;
    }
    .a-menu::-webkit-scrollbar-thumb {
        background: #d9dde6;
        transition: ease-in-out 0.32s;
    }
    .a-menu::-webkit-scrollbar-thumb:hover {
        background: #cbd0db;
        transition: ease-in-out 0.32s;
    }
    .a-menu:hover {
        overflow-y: auto;
        transition: ease-in-out 0.16s;
    }
    .a-menu .a-list {
        display: flex;
        flex-direction: column;
        padding-top: 4px;
        box-sizing: border-box;
    }
    .a-menu .a-list.is-child .a-list-content a {
        padding: 2px 0 2px 16px;
        font-size: 12px;
    }
    .a-menu .a-list .a-list-content {
        display: inline-flex;
        flex-direction: column;
        margin-left: -2px;
        font-size: 14px;
        color: #666666;
    }
    .a-menu .a-list .a-list-content .a-list .a-list-content {
        display: block;
        position: relative;
    }
    .a-menu .a-list .a-list-content .a-list .a-list-content a {
        padding-right: 0;
    }
    .a-menu .a-list .a-list-content .a-list .a-list-content.colorActive::after {
        background-color: #248bff;
    }
    .a-menu .a-list .a-list-content .a-list .a-list-content::after {
        content: '';
        display: inline-block;
        position: absolute;
        left: 4px;
        top: 0;
        bottom: 0;
        width: 1px;
        background-color: #dfdfdf;
    }
    .a-menu .a-list .a-list-content a {
        display: inline-flex;
        align-items: center;
        margin: 4px 0;
        padding-right: 20px;
        position: relative;
        color: #999;
        overflow: hidden;
    }
    .a-menu .a-list .a-list-content a .space-inner {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .a-menu .a-list .a-list-content a:hover {
        color: #165dff;
    }
    .a-menu .a-list .a-list-content a:hover.has-children::before {
        border-bottom-color: #248bff;
        border-right-color: #248bff;
    }
    .a-menu .a-list .a-list-content a.has-children {
        display: flex;
        color: #000;
    }
    .a-menu .a-list .a-list-content a.has-children::before {
        content: '';
        display: flex;
        width: 6px;
        height: 6px;
        position: absolute;
        right: 0px;
        top: 2px;
        transform: rotate(45deg);
        border-bottom: 1px solid #C9CDD4;
        border-right: 1px solid #C9CDD4;
    }
    .a-menu .a-list .a-list-content a.is-open::before {
        top: 6px;
        transform: rotate(-135deg);
    }
    .a-menu .a-list .a-list-content a:not(.is-open) + .a-list {
        display: none;
    }
    .a-menu .a-list .a-list-content a.activeLink {
        color: #248bff;
    }
    .a-menu > .a-list > .a-list-content > a {
        font-size: 14px;
        color: #000;
    }

</style>
<body>
<div class="a-menu" id="menuBox">
</div>


<script>


    let contentHtmlStr = `<div data-inline-code-theme="red" data-code-block-theme="tomorrow night"><h1 data-lines="1" data-sign="70af8ccd3da1fa06b19ac1bbab643567" id="一级目录"><a href="#一级目录" class="anchor"></a><strong>一级目录</strong></h1><h2 data-lines="1" data-sign="b961c3193476d88b1d341e95b5d80327" id="二级目录"><a href="#二级目录" class="anchor"></a><strong>二级目录</strong></h2><h1 data-lines="1" data-sign="25aae3b2a2128daa60fd958f2bcd3d2d" id="一级目录-3"><a href="#一级目录-3" class="anchor"></a><strong>一级目录</strong></h1><h3 data-lines="1" data-sign="08fddbf5db1e7127bc127ee6d4727500" id="三级目录"><a href="#三级目录" class="anchor"></a><strong>三级目录</strong></h3><h1 data-lines="1" data-sign="e7b867ff368ac6b72c919ed3512bad68" id="一级目录-4"><a href="#一级目录-4" class="anchor"></a><strong>一级目录</strong></h1><h2 data-lines="1" data-sign="5e46a853fffe62d8533d1ec70c30d75e" id="二级目录-3"><a href="#二级目录-3" class="anchor"></a><strong>二级目录</strong></h2><h3 data-lines="1" data-sign="35c88e5d8a4fe471473ff3399fb80d67" id="三级目录-3"><a href="#三级目录-3" class="anchor"></a><strong>三级目录</strong></h3><h2 data-lines="1" data-sign="e84f6131c5fa7a71ccce5ede7f6d4f44" id="二级目录-4"><a href="#二级目录-4" class="anchor"></a><strong>二级目录</strong></h2><h3 data-lines="1" data-sign="42140303320892247936dbddd25f6437" id="三级目录-4"><a href="#三级目录-4" class="anchor"></a><strong>三级目录</strong></h3><h2 data-lines="1" data-sign="6841f56875dca1f8202c2d107728d51e" id="二级目录-5"><a href="#二级目录-5" class="anchor"></a><strong>二级目录</strong></h2><h3 data-lines="1" data-sign="e2e87e66fe9a4d690d5b39295f68db9b" id="三级目录-5"><a href="#三级目录-5" class="anchor"></a><strong>三级目录</strong></h3><h4 data-lines="1" data-sign="60fc5695a612f0345a887c03e2270fc2" id="四级目录"><a href="#四级目录" class="anchor"></a><strong>四级目录</strong></h4><h3 data-lines="1" data-sign="44539014907c9a6576c85d3f680f7451" id="三级目录-6"><a href="#三级目录-6" class="anchor"></a><strong>三级目录</strong></h3><h3 data-lines="1" data-sign="b46bc9232cf0159857bf60b3aa2c5195" id="三级目录-7"><a href="#三级目录-7" class="anchor"></a><strong>三级目录</strong></h3><h1 data-lines="1" data-sign="d71970fb2368a14fc5aa82e29129173c" id="一级目录-5"><a href="#一级目录-5" class="anchor"></a><strong>一级目录</strong></h1><h4 data-lines="1" data-sign="f9812f5521a2221003e49755afcc29c6" id="四级目录-3"><a href="#四级目录-3" class="anchor"></a><strong>四级目录</strong></h4><h1 data-lines="1" data-sign="e46816d23a866b8523bf08e1d391ab7d" id="一级目录-6"><a href="#一级目录-6" class="anchor"></a><strong>一级目录</strong></h1><h3 data-lines="1" data-sign="67fbe72a8d079f663ccbda69ee668da0" id="三级目录-8"><a href="#三级目录-8" class="anchor"></a><strong>三级目录</strong></h3><h1 data-lines="1" data-sign="3c5a1ea69b73db6836bfbf3bb2224c26" id="一级目录-7"><a href="#一级目录-7" class="anchor"></a><strong>一级目录</strong></h1><h2 data-lines="1" data-sign="3fc89b827eb2b5a4d54859e843cff7d5" id="二级目录-6"><a href="#二级目录-6" class="anchor"></a><strong>二级目录</strong></h2><h1 data-lines="1" data-sign="5dee0f6d3936fc8911a76a904223b3d9" id="一级目录-8"><a href="#一级目录-8" class="anchor"></a><strong>一级目录</strong></h1><h4 data-lines="1" data-sign="13b7a1aecb892fe01daf9d307832d5c0" id="四级目录-4"><a href="#四级目录-4" class="anchor"></a><strong>四级目录</strong></h4><h1 data-lines="1" data-sign="d1dbf5c2c258c99a96adc878965ef1f0" id="一级目录-9"><a href="#一级目录-9" class="anchor"></a><strong>一级目录</strong></h1><h3 data-lines="1" data-sign="a67216f009b8c8d794a9b7fbfab117ce" id="三级目录-9"><a href="#三级目录-9" class="anchor"></a><strong>三级目录</strong></h3><h1 data-lines="1" data-sign="0eaafae5a4e4f62a672c9829163a936b" id="一级目录-10"><a href="#一级目录-10" class="anchor"></a><strong>一级目录</strong></h1><h2 data-lines="1" data-sign="56a41f5ab1aa716f10386e3198e1894d" id="二级目录-7"><a href="#二级目录-7" class="anchor"></a><strong>二级目录</strong></h2><h3 data-lines="1" data-sign="15462102af967ea2a4d18f7cdab278e0" id="三级目录-10"><a href="#三级目录-10" class="anchor"></a><strong>三级目录</strong></h3><h4 data-lines="1" data-sign="d20063c4955759c16f40a54a6fe0e223" id="四级目录-5"><a href="#四级目录-5" class="anchor"></a><strong>四级目录</strong></h4><h5 data-lines="1" data-sign="4f474aa0317f5fab32746ed19e8b0a08" id="五级目录"><a href="#五级目录" class="anchor"></a><strong>五级目录</strong></h5><h6 data-lines="1" data-sign="e54b3244c958b60083fd0fd1f0a79448" id="六级目录"><a href="#六级目录" class="anchor"></a><strong>六级目录</strong></h6><h1 data-lines="1" data-sign="b0d40797eed445eb5e29a011e7fe1e1f" id="一级目录-11"><a href="#一级目录-11" class="anchor"></a><strong>一级目录</strong></h1><h2 data-lines="1" data-sign="f055a5ed87cfe7934262638b5ea1e076" id="二级目录-8"><a href="#二级目录-8" class="anchor"></a><strong>二级目录</strong></h2><h3 data-lines="1" data-sign="5ed4b1b9e11053095450d803a66e8595" id="三级目录-11"><a href="#三级目录-11" class="anchor"></a><strong>三级目录</strong></h3><h4 data-lines="1" data-sign="6ac151d6f6d0a08da7eda2ff1b644280" id="四级目录-6"><a href="#四级目录-6" class="anchor"></a><strong>四级目录</strong></h4><h5 data-lines="1" data-sign="de2883023dc43dda752c05db796f8739" id="五级目录-3"><a href="#五级目录-3" class="anchor"></a><strong>五级目录</strong></h5><h6 data-lines="1" data-sign="71084d01bb4fa484cab4b75b7d531bd2" id="六级目录-3"><a href="#六级目录-3" class="anchor"></a><strong>六级目录</strong></h6></div>`

    //获取当前dom应该向左边移动的位置
    function getItemPaddingLeft(item){
        let defaultVal = 16;
        let maxTreeLevel =1;
        return (defaultVal+16*(item.treeLevel-maxTreeLevel))+'px';
    }
    function getContentTree(contentHtmlStr) {
        if(!contentHtmlStr) {
            return [];
        }
        let hDomList = contentHtmlStr.match(
            /<h[1-6]{1}[^>]*>([\s\S]*?)<\/h[1-6]{1}>/g
        );
        if(!Array.isArray(hDomList)){
            return [];
        }
        const dirTree = [];
        //最大的h标签值
        let maxhLevel;
        hDomList.forEach((hDom,index)=>{
            const startIndex = hDom.indexOf("id=\"");
            const endIndex = hDom.indexOf("\">");
            if(startIndex!=-1 && endIndex!=-1){
                const re = /<h([1-6]{1})[^>]*>([\s\S]*?)<\/h[1-6]{1}>/
                let hLevel  = Number(hDom.replace(re, "$1"));
                const domId = hDom.slice(startIndex+4,endIndex);
                let titleContent = hDom.replace(re, "$2");
                // 有可能匹配到的是这样’我<strong>是</strong>h1<em>斜体</em>介绍‘ 里面还有标签(直接去掉标签)
                let title = titleContent.replaceAll(/<([\s\S]*?)>|<\/([\s\S]*?)>/g, "");
                const currentDir = {
                    id:(index+1),//用于点击的时候选中态
                    title,
                    domId,
                    hLevel,//h标签值(1,2,3,4,5,6)
                    isOpen:true
                }
                if(dirTree.length>0){
                    //查找最近的父类(将数组反转才能找到最近的)
                    //reverse会改变原数组,JSON.parse(JSON.stringify())方法去一下生成新数组
                    let reverseDirTree = JSON.parse(JSON.stringify(dirTree)).reverse();
                    let findParent = reverseDirTree.find(i=>currentDir.hLevel > i.hLevel);
                    if(findParent){
                        currentDir.pid = findParent.id;
                    }else{
                        currentDir.pid = 0;
                    }
                }else{
                    currentDir.pid = 0;
                }
                if(maxhLevel){
                    if(currentDir.hLevel < maxhLevel){
                        maxhLevel = currentDir.hLevel;
                    }
                }else{
                    maxhLevel = currentDir.hLevel;
                }
                dirTree.push(currentDir)
            }
        })
        dirTree.forEach((item)=>{
            item.parentTreeLevel = 0;//父类默认是0
            item.treeLevel = (item.hLevel - maxhLevel) +1;//目录树层级,也代表当前h标签解析的内容在父类属于第几层级,1是最大层级
            if(item.pid!==0){
                const findParent = dirTree.find(i=>i.id === item.pid);
                if(findParent){
                    if(!Array.isArray(findParent.children)){
                        findParent.children = []
                    }
                    item.parentTreeLevel = (findParent.hLevel - maxhLevel) +1;//父目录树层级,也代表当前h标签解析的内容在父类属于第几层级,1是最大层级
                    findParent.children.push(item)
                }
            }
        })
        return dirTree.filter((i)=>i.pid === 0);
    }

    let contentTree = getContentTree(contentHtmlStr);

    let contentTreeHtml = '';

    function calcContentTreeHtml(contentTree) {
        if (contentTree.length > 0) {
            contentTreeHtml +=`<div class="a-list">`
            for (let i in contentTree) {
                contentTreeHtml +=`
                   <div>
                      <div
                          style="padding-left:${getItemPaddingLeft(contentTree[i])}"
                          class="a-list-content"
                      >
                        <a class="${(Array.isArray(contentTree[i].children) && contentTree[i].children.length>0 && contentTree[i].isOpen)?'is-open':''} ${(Array.isArray(contentTree[i].children) && contentTree[i].children.length>0)?'has-children':''}">
                          <span class="space-inner" title="${contentTree[i].title}">${contentTree[i].title}</span>
                        </a>
                        </div>
                      `
                if (Array.isArray(contentTree[i].children) && contentTree[i].children.length>0) {
                    calcContentTreeHtml(contentTree[i].children);
                }
                contentTreeHtml += `</div>`;
            }
            contentTreeHtml += `</div>`;
        }
    }

    calcContentTreeHtml(contentTree);

    document.getElementById('menuBox').innerHTML = contentTreeHtml;

</script>


</body>
</html>

五.总结

   1.如果要转换成功react或者vue,只要把calcContentTreeHtml方法转换成组件就行

   2.方法一和方法二比较主要是里面的dom摆放位置不一样,方法一是因为里面的div有继承关系所以getItemPaddingLeft方法计算方式繁琐一点,方法二是加了主要是在外层加了一个div,然后计算padding-left就简单一点,但方法二的样式还需改造,方法一和方法二的差别看getItemPaddingLeft和calcContentTreeHtml方法就行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1566567.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

EXCEL-VB编程实现自动抓取多工作簿多工作表中的单元格数据

一、VB编程基础 1、 EXCEL文件启动宏设置 文件-选项-信任中心-信任中心设置-宏设置-启用所有宏 汇总文件保存必须以宏启动工作簿格式类型进行保存 2、 VB编程界面与入门 参考收藏 https://blog.csdn.net/O_MMMM_O/article/details/107260402?spm1001.2014.3001.5506 二、…

用Vue仿了一个类似抖音的App

大家好&#xff0c;我是 Java陈序员。 今天&#xff0c;给大家介绍一个基于 Vue3 实现的高仿抖音开源项目。 关注微信公众号&#xff1a;【Java陈序员】&#xff0c;获取开源项目分享、AI副业分享、超200本经典计算机电子书籍等。 项目介绍 douyin —— 一个基于 Vue、Vite 实…

解决uCharts图表在微信小程序层级过高问题

uniapp微信小程图表层级过高问题&#xff1f; 项目中涉及 uCharts图表&#xff0c;在 App/H5端均正常使用&#xff0c;微信小程序 存在层级问题&#xff01; 文章目录 uniapp微信小程图表层级过高问题&#xff1f;效果图遇到问题解决方案 啰嗦一下~&#xff0c;自己的粗心 在实…

Firefox 关键词高亮插件的简单实现

目录 1、配置 manifest.json 文件 2、编写侧边栏结构 3、查找关键词并高亮的方法 3-1&#xff09; 如果直接使用 innerHTML 进行替换 4、清除关键词高亮 5、页面脚本代码 6、参考 1、配置 manifest.json 文件 {"manifest_version": 2,"name": &quo…

HDLbits 刷题 --Always nolatches

学习: Your circuit has one 16-bit input, and four outputs. Build this circuit that recognizes these four scancodes and asserts the correct output. To avoid creating latches, all outputs must be assigned a value in all possible conditions (See also always…

内存管理是如何影响系统的性能的

大家好&#xff0c;今天给大家介绍内存管理是如何影响系统的性能的&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 内存管理对系统性能的影响至关重要&#xff0c;主要体现在以下…

课程设计项目3.2:基于振动信号分析的电机轴承故障检测

01.课程设计内容 02.代码效果图 获取代码请关注MATLAB科研小白的个人公众号&#xff08;即文章下方二维码&#xff09;&#xff0c;并回复&#xff1a;MATLAB课程设计本公众号致力于解决找代码难&#xff0c;写代码怵。各位有什么急需的代码&#xff0c;欢迎后台留言~不定时更新…

Js之运算符与表达式——②

运算符&#xff1a;也叫操作符&#xff0c;是一种符号。通过运算符可以对一个或多个值进行运算&#xff0c;并获取运算结果。 表达式&#xff1a;由数字、运算符、变量的组合&#xff08;组成的式子&#xff09;。 表达式最终都会有一个运算结果&#xff0c;我们将这个结果称…

【cache】卡常

来源于《国家集训队2024论文集》中的《论现代硬件上的常数优化》 个人总结&#xff1a; 不要开二的次幂作为维度的数组&#xff0c;否则常数会变大【存疑】。总是保证内存访问连续性更高。比如 st表&#xff0c;把log维放在第二维&#xff0c;会导致内存访问距离最大为N*LOG。…

权限提升技术:攻防实战与技巧

本次活动赠书1本&#xff0c;包邮到家。参与方式&#xff1a;点赞收藏文章即可。获奖者将以私信方式告知。 网络安全已经成为当今社会非常重要的话题&#xff0c;尤其是近几年来&#xff0c;我们目睹了越来越多的网络攻击事件&#xff0c;例如公民个人信息泄露&#xff0c;企业…

Vulnhub:MY FILE SERVER: 1

目录 信息收集 1、arp 2、nmap 3、whatweb WEB web信息收集 dirmap FTP匿名登录 enum4linux smbclient showmount FTP登录 ssh-kegen ssh登录 提权 系统信息收集 脏牛提权 get root 信息收集 1、arp ┌──(root㉿ru)-[~/kali/vulnhub] └─# arp-scan -l I…

信息咨询模块

信息资讯 资讯文章专题、互动评论、自定义多级分类&#xff0c;打造本地新闻圈 简介 本项目旨在打造一个本地新闻资讯平台&#xff0c;为用户提供以下功能&#xff1a; 资讯文章&#xff1a;提供本地新闻、生活、娱乐等各类资讯文章。专题&#xff1a;针对重要事件或热门话…

E4438C安捷伦E4438C信号发生器

181/2461/8938产品概述&#xff1a; Agilent / HP E4438C ESG 矢量信号发生器结合了出色的 RF 性能和复杂的基带生成功能&#xff0c;可在高达 6 GHz 的基带、IF 和 RF 频率上提供经过校准的测试信号。Agilent / HP E4438C ESG 矢量信号发生器提供具有任意波形和实时 I/Q 功能…

波士顿房价预测案例(python scikit-learn)---多元线性回归(多角度实验分析)

波士顿房价预测案例&#xff08;python scikit-learn&#xff09;—多元线性回归(多角度实验分析) 这次实验&#xff0c;我们主要从以下几个方面介绍&#xff1a; 一、相关框架介绍 二、数据集介绍 三、实验结果-优化算法对比实验&#xff0c;数据标准化对比实验&#xff0…

输入url到页面显示过程的优化

浏览器架构 线程&#xff1a;操作系统能够进行运算调度的最小单位。 进程&#xff1a;操作系统最核心的就是进程&#xff0c;他是操作系统进行资源分配和调度的基本单位。 一个进程就是一个程序的运行实例。启动一个程序的时候&#xff0c;操作系统会为该程序创建一块内存&a…

​AI大模型:产品经理的新助手,还是捣蛋鬼?

在科技飞速发展的今天&#xff0c;AI大模型成为了许多行业的“新宠儿”。它们不仅在语音识别、图像处理等方面表现出色&#xff0c;甚至还能写代码、写新闻&#xff0c;甚至写需求文档。这让许多产品经理心动不已&#xff0c;心想&#xff1a;有了AI大模型的帮助&#xff0c;岂…

完全没想到docker启动败在了这里!

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 故事背景 前几天帮同事部署一个环境&#xff0c;用他写的安装脚本部署&#xff0c;其中一台服务器就需要安装docker&#xff0c…

Day43 动态规划 part05

Day43 动态规划 part05 1049.最后一块石头的重量II 我的思路: 提示说和划分两个和相等的子集差不多&#xff0c;猛然想到&#xff0c;这道题不就是划分子集&#xff0c;用sum - 和最大*2 代码就是划分和相同的子集的变形 解答&#xff1a; class Solution {public int last…

jsp实现增删改查——(三)用Echarts图表统计学生信息

学生信息CRUD——Echarts显示生活费 目录结构 创建一个js文件夹&#xff0c;将echarts.min.js放到里面。 功能实现 与之前我们写的jsp文件&#xff08;含有html代码、Java代码&#xff09;不同的是&#xff0c;实现Echarts对生活费的显示&#xff0c;需要调用echarts.min.js…

MySQL事务以及并发访问隔离级别

MySQL事务以及并发问题 事务1.什么是事务2.MySQL如何开启事务3.事务提交方式4.事务原理5.事务的四大特性&#xff08;ACID&#xff09; 事务并发问题1.并发引起的三个问题2.事务隔离级别 事务 在 MySQL 中&#xff0c;事务支持是在引擎层实现的。MySQL 是一个支持多引擎的系统&…