在富文本辑编器里面写文章时,我们通常会使用h1-h6标签来定义章节标题。在用户端显示时希望展示目录结构。如图;
在这里插入图片描述
这里生成目录结构可以借助第三方插件来实现,但某些情况下不希望引用太多的第三方资源就需要自己实现一个。
核心思路:拿到所有的h标签的节点集合,这个集合就是内容(平级)的目录,我们只需要将平级目录根据标签的(level 等级)/大小,设定一个缩进的长度乘标签大小就可生成有层次的目录结果。为了能使用使用瞄点定位,故每个节点设定一个id
核心处理代码

	//拿到文章内容的节点
               let parent = document.getElementById("previewer")
               //拿到文章里面的h1-h6标签
                let doms = parent.querySelectorAll('h1,h2,h3,h4,h5,h6');
                let hEles = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
                //结果集
                let catalogue = [];
                let index = 0;
                if(doms.length>0){
                    doms.forEach(element => {
                        var nodetext = element.innerHTML.replace(/<\/?[^>]+>/g, "");
                        nodetext = nodetext );
                        let name = element.nodeName.toLowerCase();
                        if (hEles.includes(name)) {
                            index++;
                            let id = `catalogue_${index}`;
                            //为当前节点添加id,方便使用瞄点定位
                            element.setAttribute("id", id)
                            let level = name.replace("h", "");
                            catalogue.push({ id: id, key: name, title: nodetext, level: Number(level) });
                        }
                    });

以下是vue版本的实现

let data = reactive({ news: {}, loading: true, catalogue: [] })
    const { proxy } = getCurrentInstance()
    let id = proxy.$root.$router.currentRoute.value.params.id
    onMounted(() => {
        proxy.$http.get(`/api/blog/news/${id}`).then((res) => {
            data.news = res;
            document.title = res.title;
        });

    })
  nextTick(() => {
        setTimeout(() => {
            //等待dom 加载完成
            uParse("#previewer", {
                rootPath: 'https://cdn.jsdelivr.net/gh/t-jian/static/theme/ueditor-1.4.3/',
                liiconpath: 'https://cdn.jsdelivr.net/gh/t-jian/static/theme/ueditor-1.4.3/listicon/',
            });
            setTimeout(() => {
                //防止闪烁
                data.loading = false;
                //目录导航
                let parent = document.getElementById("previewer")
                let doms = parent.querySelectorAll('h1,h2,h3,h4,h5,h6');
                let hEles = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
                let catalogue = [];
                let index = 0;
                if(doms.length>0){
                    doms.forEach(element => {
                        var nodetext = element.innerHTML.replace(/<\/?[^>]+>/g, "");
                        nodetext = nodetext.replace(/&nbsp;/ig, "");
                        let name = element.nodeName.toLowerCase();
                        if (hEles.includes(name)) {
                            index++;
                            let id = `catalogue_${index}`;
                            element.setAttribute("id", id)
                            let level = name.replace("h", "");
                            catalogue.push({ id: id, key: name, title: nodetext, level: Number(level) });
                        }
                    });
                    data.catalogue = catalogue;
                }
            }, 100);
        }, 300);

 <div class="item-card rt-panel">
        <div class="nav-title">文章导航</div>
        <ul class="catalogue-box">
          <li v-for="item in catalogue" :key="item">
           <a :href="'#'+item.id"><span :style="'margin-left:'+(4*item.level)+'px;'">{{item.title}}</span> </a> 
          </li>
        </ul>
      </div>
Logo

前往低代码交流专区

更多推荐