前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何根据页面标签自动生成文章目录?分析+代码详解

如何根据页面标签自动生成文章目录?分析+代码详解

原创
作者头像
Mintimate
修改2021-10-12 14:17:22
5K3
修改2021-10-12 14:17:22
举报

作者:Mintimate

博客:https://www.mintimate.cn

Mintimate's Blog,只为与你分享

封面嗷(这次是不是很简约……)
封面嗷(这次是不是很简约……)

文章目录

文章目录功能大家再熟悉不过了吧,主要用于长篇文章、教程内:用户可以根据自己需求,点击目录进行跳转。

常见的目录效果:

腾讯云社区的文章目录
腾讯云社区的文章目录
Mintimate's Blog的文章目录
Mintimate's Blog的文章目录

这样的文章目录,难道要写到数据库里么?

当然不是 ,这个肯定是前端人员实现的。而且用简单的JavaScript就可以实现。

“Any application thatcanbe written in JavaScript,willeventually be written in JavaScript.” — — Jeff Atwood

问题分析

一般需要生成目录的文章,标题都是需要标题和章节目录,所以必须要要有特定的标签修饰。举个例子,我这个网站是这样的:

文章结构
文章结构

按F12查看其源码:

源码查看
源码查看

分析源码:

  • 网页应该是根据Markdown生成的
  • 文章分目录,使用html的<h1><h2><h3>标签,进行分层。
  • 每个<h>标题标签,自带ID,可以使用“#”进行文章定位

综上,就很清晰了:

  • 提取内容部分的<h1>~<h3>标签(三层的目录……不多不少,嘿嘿),生成tree结构
  • 提取/放置标签ID,作为目录索引,便于目录功能的文章定位

接下来,就叫大家分步操作。

封装标题标签

封装标题标签的目的很简单,最终的效果:

左侧:封装的集合。右侧:页面DOM内容
左侧:封装的集合。右侧:页面DOM内容

为了实现这样的效果;首先,我们要在页面加载后,遍历文章:

  • 如果你使用原生JavaScript,并没有使用任何框架,或者是JQ,那么就写在</body>前即可。
  • 如果你使用的是Vue,那么写在method,或者mounted里调用都可以,最好加上this.$nextTick(()进行修饰,保证页面加载成功。

遍历文章,很简单,我们使用childNodes方法和foreach循坏即可。

childNodes 属性返回节点的子节点集合,以 NodeList 对象。

实操演示如下。

遍历文章

原生JavaScript

单层包括,也就是需要生成目录的文章,外层有一个<div></div>或其他双标签进行嵌套,如:

只有一个div双标签进行嵌套
只有一个div双标签进行嵌套

这个时候我们在根标签加上一个ID即可:

加上id
加上id

之后,在JavaScript内即可获取子元素:

// 获取文章内容
const article_content = document.getElementById('content');
// 文章内容标签遍历
article_content.childNodes.forEach((e, index) => {
        //具体执行步骤,比如:打印看看
        console.log(e)
}
效果
效果

这样就遍历成功了。

Vue实现

这里在讲一下Vue如何实现,Vue不提倡我们直接操作页面DOM元素,所以这里我们可以在组件上加ref标签进行关联:

ref标签关联
ref标签关联

获取文章内容,就可以:

// 根据ref获取内容
const article_content = this.$refs.markdownContent;

但是,一般组件生成后,都是多标签进行包裹,要遍历文章标签,就需要children进行指定,比如:

实际效果
实际效果

所以,我们就可以这样遍历:

article_content.children[0].children[0].childNodes.forEach((e, index) => {
    //具体执行步骤,比如:打印看看
        console.log(e)
}

这样就遍历成功了。

效果
效果

封装元素

接下来,看到这些#text是不是束手无策?其实也很简单,我们进一步进行解析即可。

首先在循坏遍历的外侧,添加一个数组,如果页面元素标签,在这个数组范围内,就提取到标签集合并生成一个对象丢到titles内:

 // 哈哈,三级目录差不多了吧。云+社区也是三级目录~~~
 const titleTag = ["H1", "H2", "H3"];
 let titles = [];

在遍历文章内容时,就可以判断标签是不是在数组内:

if (titleTag.includes(e.nodeName)) {
  //具体封装过程
}

之后,我们看看具体封装过程的逻辑实现。

首先是给文章每个<h>标签,加上id,id的生成,我们使用变量时的index即可:

const id = "header-" + index;
// 设置元素id
 e.setAttribute("id", id);

之后,就是把<h>标签,解析成一个树,丢到titles内:

titles.push({
  id: id,
  title: e.innerHTML,
  level: Number(e.nodeName.substring(1, 2)),
  nodeName: e.nodeName
});

最终代码效果:

< script type = "text/javascript" > 
const article_content = document.getElementById('content');
const titleTag = ["H1", "H2", "H3"];
let titles = [];
article_content.childNodes.forEach((e, index) = >{
	if (titleTag.includes(e.nodeName)) {
		const id = "header-" + index;
		e.setAttribute("id", id);
		titles.push({
			id: id,
			title: e.innerHTML,
			level: Number(e.nodeName.substring(1, 2)),
			nodeName: e.nodeName
		});
	}
});
const catalog = titles;
console.log(catalog); 
< /script>

页面打印效果:

效果
效果

同时,Vue内也差不多:

Vue内
Vue内

页面渲染

最后,我们看看页面的渲染,页面渲染就可以根据喜好渲染了。比如我的(Vue工程):

Vue内
Vue内

Vue工程可以使用v-for进行遍历,还是很简单的。

如果是原生JavaScript,就要自己拼接了……

// 原生JavaScript遍历
for (index in catalog) {
    document.getElementById('cataLog').innerHTML += "<li style='padding-left: " + (catalog[index].level * 22 - 22) + "px;'>" + "<a href='#" + catalog[index].id + "'>" + catalog[index].title + "</a>""</li>"
};

同时添加样式:

.catalog{
  position: sticky;
}

最终效果:

目录就有了嗷(Vue)
目录就有了嗷(Vue)
目录就有了嗷(JavaScript)
目录就有了嗷(JavaScript)

END

本次的前端分享就到这边~(真没想到,我一个学Linux和后端代码的“小学生”,有一天能讲前端代码实现……)

完结撒花
完结撒花

总的来说,生成目录还是很简单的。可能不是最优解,但是确实是个不错的方法。有什么问题,可以再评论留言嗷。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 问题分析
  • 封装标题标签
    • 遍历文章
      • 原生JavaScript
      • Vue实现
    • 封装元素
    • 页面渲染
    • END
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档