文章出自个人博客https://knightyun.github.io/2020/01/14/website-add-category,转载请申明


概述

之前有写文章探索如何给个人博客网站添加文章搜索功能,可以方便的通过关键词检索相关文章,现在再来探索一下另一个功能,即给文章添加目录导航;对于篇幅较短的文章,目录的有无影响不大,但是当文章篇幅过长时,一个能提供预览和跳转的目录结构预览,就显得意义重大了,接下来就来一步步将它实现出来;

原理

样式

实现功能必先思考其原理,目录预览其实就是一块内容,包含当前页面不同级别的标题的组合,并结构化的展示出来,首先我们可以参考一些网站的做法,比如 CSDN 的博客文章就有配置目录插件,下面就是我的某篇文章的目录预览图:

它们的目录插件就是右边侧栏的一个按钮,鼠标放上去就会显示一个侧栏,内容就是当前文章的小标题的集合,不同级别的标题对应这不同程度的缩进,并且点击每个标题都会有相应的页面跳转,这也基本是我们常见而熟悉的目录形式,那么我们就以此为参考来实现;

目录获取

想要生成这么一个目录之前,当然是要先获取目录的内容,前面讲过,目录的内容就是当前文章的所有标题的集合,而我们知道,在 HTML 中标题相关的标签是 h1, h2, h3, h4, h5, h6 这几个,所以直接获取它们就行了,比如:

// 获取所有的标签名为 h1 的元素
document.querySelectorAll('h1');
// 获取所有的标签名为 h1 - h6 的元素
document.querySelectorAll('h1, h2, h3, h4, h5, h6');

获取内容是一个数组,包含所有标题节点;接下来的问题就是考虑如何结构化存储,这样便于理解的同时又方便后期的读取,所谓结构化,即目录本身就是一类 结构,比如,目录包含多个一级标题,同时某些以及标题可能还有多个二级标题,甚至再向下延伸出三级标题等等,类似下面的结构:

├─ 一级标题 1
│  └─ 二级标题 1
│      └─ 三级标题 1
│          └─ 四级标题
├─ 一级标题 2
│  └─ 二级标题 1
├─ 一级标题 3
│  ├─ 二级标题 1
│  ├─ 二级标题 2
│  ├─ 二级标题 3
│  │  ├─ 三级标题 1
│  │  ├─ 三级标题 2
... ...

理论的做法就是以树结构保存获取的标题,类似于下面这种:

[{node: 'h1Node', // 一级标题 1 对应的节点child: [{node: 'h2Node', // 二级标题 1child: [{node: 'h3Node', // 三级标题 1child: []}]},{node: 'h2Node', // 二级标题 2child: []}]
},
{node: 'h1Node', // 一级标题 2child: [{node: 'h2Node', // 二级标题 1child: []}]
},
{node: 'h1Node', // 一级标题 3child: []
}]

看着还是比较复杂的,耗费的空间也较大,需要递归式获取,最后也要递归式的输出,一般文章目录包含的标题数量也是较少的,所以暂且不用这种结构来保存,可以换一种简单的思路,即我们最后生成该目录时可以选择一行一行递进的输出,即设计如下结构:

['h1Node', // 一级标题 1 对应的元素节点'h2Node', // 二级标题 1 (隶属于一级标题 1)'h3Node', // 三级标题 1 (隶属于二级标题 1)'h2Node', // 二级标题 2 (隶属于一级标题 1)'h1Node', // 一级标题 2'h2Node', // 二级标题 1 (隶属于一级标题 2)'h1Node'  // 一级标题 3
]

因为我们只要求最终能输出一列格式化的目录,即挨个依次输出,所以只需以此存储即可,这样占用的空间和复杂度都有所减少;

目录生成

最终展示效果设定为最上面的样图所示,按照之前设计的存储结构,遍历该数组一行一行打印出来即可;关于不用级别的标题应用不同程度的缩进,可以巧妙利用一下元素节点的 nodeName 这个属性,比如元素节点 <h1></h1> 对应的 nodeName 就是 H1h2 就对应 H2,以此类推,我们就利用该值最后的那个数字,乘以一个固定缩进值,这样级别递增的标题节点也就拥有了递增的缩进值,最后样式部分就可以利用 padding-left 来实现缩进,js 代码的实现思路如下:

// node 为标题节点,32 是标题级别增加而多缩进的值
node.style.paddingLeft = node.nodeName.slice(-1) * 32 + 'px';

对于点击标题跳转到文章对应标题所在位置这个功能,实现也比较简单,设置对应 锚点 即可,也就是标题元素需要设置一个 id 属性值,然后给点击的 <a> 标签的 href 属性也设置为这个 id 值即可,例如:

<h1 id="my-h1">一级标题</h1><a href="my-h1">点我跳转到一级标题</a>

具体实现

第三方库

避免重复造轮子,一些基础的格式化样式就交给第三方库去解决吧,这里使用的是 Materialize 这个库,安装和引用教程去官网:https://materializecss.com 查看;

HTML部分

具体参考代码与说明如下:

<!-- 引用第三方库 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"><!-- 固定于屏幕右下方的一个悬浮按钮 -->
<div class="fixed-action-btn"><a class="btn-floating btn-large blue z-depth-4"><i class="large material-icons">apps</i></a><ul><!-- 文章目录按钮 --><li class="category-btn hide"><a class="sidenav-trigger btn-floating blue lighten-2" data-target="category"><i class="material-icons">format_list_bulleted</i></a></li><!-- 下面也可以添加其他按钮,如返回文章顶部等--><li><a class="btn-floating blue lighten-2" href="javascript: scrollTo(0, 0);"><i class="material-icons">publish</i></a></li></ul>
</div><!-- 文章目录侧栏 -->
<ul id="category" class="hide sidenav grey lighten-4 grey-text text-darken-3"><li><p class="center-align">目录</p></li>
</ul><!-- 下面的元素中存放文章内容 -->
<div id="post-content"><!-- 文章内容,需要注意的只有,为不同的标题元素设置不同的 id 属性以实现跳转 --><!-- 以下为示例内容 --><h1 id="t1">Title 1</h1><p>Hello World!</p><p>Hello World!</p><h2 id="t11">Title 1</h2><p>Hello World!</p><p>Hello World!</p><p>Hello World!</p><p>Hello World!</p><p>Hello World!</p><h2 id="t12">Title 1</h2><h3 id="t121">Title 1</h3><p>Hello World!</p><p>Hello World!</p><p>Hello World!</p><h1 id="t2">Title 2</h1><p>Hello World!</p><p>Hello World!</p><p>Hello World!</p><p>Hello World!</p><p>Hello World!</p><h1 id="t3">Title 3</h1><p>Hello World!</p><h2 id="t31">Title 3</h2><p>Hello World!</p><p>Hello World!</p><p>Hello World!</p><p>Hello World!</p><p>Hello World!</p><p>Hello World!</p><p>Hello World!</p>
</div>

CSS部分

样式部分因人而异,可以自行设计调整,以下为参考:

#category li a:before { /* 添加一个折叠符号,为了好看 */content: "∟";position: absolute;left: 10px;bottom: 5px;font-size: 12px;
}

JavaScript部分

该部分就是核心所在了,对应上面的 HTML 和 CSS 部分,实现如下:

// 初始化第三方库的插件
M.AutoInit();
document.addEventListener('DOMContentLoaded', function () {var elemCategory = document.querySelector('#category');M.Sidenav.init(elemCategory, {'edge': 'right' // right 表示在右侧栏显示,left 则表示在左边显示});
});var postContent = document.querySelector('#post-content');if (postContent) { // 存在文章内容var categories = postContent.querySelectorAll('h1, h2, h3, h4, h5, h6');if (categories.length > 0) { // 文章存在标题var category = document.querySelector('#category'),categoryBtn = document.querySelector('.category-btn');var li = document.createElement('li'),a = document.createElement('a');a.className = 'waves-effect';// 存在目录则显示目录按钮和侧栏category.classList.remove('hide');categoryBtn.classList.remove('hide');categories.forEach(node => {// 每次 cloneNode 取代 createElement// 因为克隆一个元素快于创建一个元素var _li = li.cloneNode(false),_a = a.cloneNode(false);_a.innerText = node.innerText;// 为标题设置跳转链接_a.href = '#' + node.id;_li.appendChild(_a);// 为不同级别标题应用不同的缩进_li.style.paddingLeft = node.nodeName.slice(-1) * 32 + 'px';category.appendChild(_li);})}
}

效果

最后附上几张本人博客网站实现的最终效果图,也欢迎点击 https://knightyun.github.io 前往访问 ^_^


技术文章推送 手机、电脑实用软件分享

个人博客网站文章添加目录导航相关推荐

  1. 为博客的文章添加二维码

    文章地址:http://www.xiabingbao.com/blogs/2016/08/31/blogs-qrcode.html 为博客的文章添加了一个生成二维码的功能,可以在扫描二维码后在移动端进 ...

  2. 为博客园博文添加目录的两种方法

    准备工作: 确认你的博客有js权限,可以设计界面风格.这个需要跟管理员申请. 方法一:在正文上方直接添加目录 1.把下面的脚本添加到博客后台设置里的"页脚html代码"里头: 1 ...

  3. 博客园文章自动生成导航目录

    文章如果比较长的话,子标题很难找,文章结构没法一目了然,如果有一个导航目录靠在边栏就好了 看了很多园子里其他的文章,js和css挺长的,怕用不好,干脆自己尝试写一个 一.要实现的功能 1.自动生成 不 ...

  4. 博客园文章添加版权信息的方法

    管理--操作--博客签名,进入到制作签名的页面.在"内容"的文本框输入如下信息并替换相应的文字: <div>作者:<a href="http://www ...

  5. Hexo博客yilia主题文章添加目录

    参考文章 添加目录的文章有一些是自己添加css文件和修主题配置 作者也更新了文章大体目录的功能 打开配置文件themes/yilia/_config.yml 你可以选择toc设置为1 或者2 toc: ...

  6. 在文章右上角添加目录导航

    在文章右上角添加目录导航 1.添加css代码到"页面定制css代码" 1 /*生成博客目录的CSS*/ 2 #uprightsideBar{ 3 font-size:12px; 4 ...

  7. github+hexo搭建自己的博客网站(六)进阶配置(搜索引擎收录,优化你的url,添加RSS)...

    详细的可以查看hexo博客的演示:https://saucxs.github.io/ 绑定了域名: http://www.chengxinsong.cn hexo+github博客网站源码(可以clo ...

  8. 老徐的博客:文章目录

    Posted on 2009-05-22 23:35 Frank Xu Lei 阅读(1850) 评论(18)  编辑 收藏 网摘 所属分类: 老徐的博客:文章目录, SOA and EAI 老徐的博 ...

  9. 在线文本替换工具 、支持正则表达式(博客园文章里添加Javascript或<script>语句)

    概况与介绍 在博客园发布一篇文章,文章就是<在线文本替换工具 .支持正则表达式>https://www.cnblogs.com/lsllll44/articles/15522697.htm ...

最新文章

  1. Windows10上使用VS2017编译OpenCV3.4.2+OpenCV_Contrib3.4.2+Python3.6.2操作步骤
  2. posix_kill 信号
  3. caffe windows学习:第一个测试程序
  4. 统计用户在某一页停留的时间
  5. html用bmob做留言,bmob js-sdk 在vue中的使用教程
  6. 论文浅尝 | CoRR - 面向复杂知识问答的框架语义解析方法
  7. R语言:ggplot2精细化绘图——以实用商业化图表绘图为例(转)
  8. 阿里北大:深度哈希算法最新综述
  9. 03-25 内存统计
  10. 微会显示服务器当前线路忙,免费电话之争:触宝电话/微会谁更强
  11. linux网络安装mysql_linux系统安装mysql
  12. java将数据从外部读入到程序称为_java复习与练习答案beta3(基本可信~~)
  13. TMG自动发现功能配置正常,但客户端却检测不到TMG服务器。
  14. postgresql注册表删除_【清理注册表】删除SQL Server注册表
  15. 关于近段时间学习历史的点滴记录
  16. html去除图片背景颜色,canvas实现图片背景色去色变透明 » 张鑫旭-鑫空间-鑫生活...
  17. 4G模块 | 基于4G Cat.1的内网穿透实践
  18. USI环旭电子推出信用卡大小的SiPSet笔记本电脑主板
  19. 12个成功案例,告诉你提升客户体验新方法!
  20. JPA @PersistenceContext和@Transactional Annotation

热门文章

  1. 李洪强iOS开发之OC[012] -类的声明实现小结
  2. (AirWatch 系列之一)企业移动计算的集大成者--Airwatch简介
  3. C#中yield用法
  4. 通过代码解决全角问题类调用法
  5. 宽带路由器常见故障排除
  6. jsapi.php 参数设置,统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!...
  7. SpringMVC系列一
  8. 4x3矩阵键盘扫描法c语言,四种4*4矩阵键盘的扫描方法
  9. sublime编辑器无法正常打印中文问题解决
  10. Linux系统安装管理