JS 生成文章目录树

2023-09-24 26 0

原文地址: JS 生成文章目录树

我们在网页浏览文章时,会发现在页面上总有一块固定区域,用来展示文章的目录结构,以帮助我们快速定位到对应的内容。今天我们就用 JavaScript 来实现文章目录树的功能。

功能需求

1). 根据文章的 <h>标签生成目录树;

2). 点击目录,页面可以翻动到对应的位置;

3). 滚动页面,对应的目录可以动态高亮;

实现思路

假设后端返回的 html 数据如下:

<div class="htmlbox"><h1>标题一</h1><p>…………</p><h2>标题1.1</h2><p>…………</p><h2>标题1.2</h2><p>…………</p><h1>标题二</h1><p>…………</p><h2>标题2.1</h2><p>…………</p><h3>标题2.2.1</h3><p>…………</p><p>…………</p>
</div>

根据文章的 <h>标签生成目录树

创建一个 <ul>元素作为目录的容器,然后获取到 .htmlbox 中所有的 <h> 标签,通过遍历 <h> 获取其内容,并且生成与之对应的 <li>标签,最后把 <li> 依次添加到 <ul>中。

点击目录,页面翻动到对应的位置

在遍历 <h>元素过程中,需要为其生成一个唯一的 id,并且需要将此 id 通过自定义属性绑定在动态创建的 <li>元素上,目的是为了在点击 <li>的时候,可以根据此自定义属性找到对应的 <h>标签,最后通过 scrollIntoView() 进行页面滚动。

滚动页面,对应的目录可以动态高亮

需要监听浏览器的 scroll 事件,获取滚动条距离页面顶部的位置。然后倒叙遍历 <h>元素时,判断滚动条到浏览器顶部的距离是否 >= 当前 <h> 标签到浏览器顶部的距离。如果条件成立,我们就把此 <h> 对应的 <li> 给设置高亮。(必须要倒叙遍历,否则高亮的 <li> 永远都是第一个)。

代码实现

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>目录</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}/* 摸你文章每个段落的高度 */p {height: 500px;background-color: beige;}/* 文章整体宽度 */.htmlbox {width: 700px;}/* 目录容器样式 */.catalogbox {position: fixed;top: 10px;right: 10px;width: 300px;max-height: 80vh;}/* 高亮目录样式 */.current-catalog {background-color: #0099dd;color: #fff;}</style>
</head>
<body><div class="htmlbox"><h1>标题一</h1><p>…………</p><h2>标题1.1</h2><p>…………</p><h2>标题1.2</h2><p>…………</p><h1>标题二</h1><p>…………</p><h2>标题2.1</h2><p>…………</p><h3>标题2.2.1</h3><p>…………</p><p>…………</p></div><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script><script type="text/javascript">$(function() {// *************功能一:生成目录******************************var eles = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']// 获取文章里 h1 到 h6 的元素var doms = document.querySelector('.htmlbox').querySelectorAll(eles.toString())if (! doms || ! doms.length) {return ;}// 创建目录盒子let $ul = $('<ul class="catalogbox"></ul>')// 目录的下标let index = 0for (let h of doms) {let tag = h.nodeName.toLowerCase()if (! eles.includes(tag)) {continue ;}// 生成每个目录的id,绑定在 h 标签上let id = `catalog_${++index}`h.setAttribute('id', id)// 获取 h 标签的内容let text = h.innerHTML.replace(/<\/?[^>]+>/g, '')// 生成 li 元素,需要绑定h 的id,以便于点击目录时可以找到对应的 h 标签let $li = `<li  catalog="${id}">${text}</li>`$ul.append($li)}$('body').append($ul)// *************功能二:点击目录滚动到对应区域******************************$('li[catalog]').on('click', function() {// 获取每个li 上绑定的catalog值,对应着唯一的 h 标签let id = $(this).attr('catalog')document.querySelector(`#${id}`).scrollIntoView({behavior: 'smooth'})})// *************功能三:目录跟随滚动高亮******************************window.addEventListener('scroll', function() {// 获取浏览器滚动条距离顶部的高度let scroll = document.documentElement.scrollTop || document.body.scrollTopfor (let i = doms.length - 1; i >= 0 ; i--) {// 倒叙遍历所有的 h 标签,如果滚动条的 scrollTop 刚刚大于 h 区域的 offsetTop,// 将此h 标签对应的 目录 设置为高亮if (parseInt(scroll) >= Math.ceil(doms[i].offsetTop) ) {let id = doms[i].getAttribute("id")$('li[catalog]').each(function() {if ($(this).attr('catalog') === id) {$(this).addClass('current-catalog')} else {$(this).removeClass('current-catalog')}})break ;}}})})</script></body>
</html>

运行效果如下:

tree.gif

补充:让目录有层次感

不同级别的目录缩进也不同。比如一级标题<h1> 我们可以设置其 padding-left10px,二级标题 <h2> 需要设置 padding-left20px,依次类推。

实现此功能思路也很简单:在js中创建 <li>的时候,获取与之对应的 <h>标签中的数字,然后给 <li> 绑定一个样式,最后我们只需要在 css中对样式进行设置即可。比如 <h1> 标签对应的 <li>,我们可以给其添加 style="--level: 1"<h2> 标签对应的 <li>,我们可以给其添加 style="--level: 2",然后在 css中通过 var() 函数去设置样式。

关于 var() 函数,可以看 CSS var() 函数 一文。

代码实现

js:

// 获取当前目录级别,需要根据目录级别设置css
let level = tag.replace('h', '')
// 生成 li 元素
let $li = `<li style="--level: ${level}" catalog="${id}"">${text}</li>`

css:

li[catalog] {padding-left: calc(var(--level) * 10px) ;
}
代码编程
赞赏

相关文章

网络教育统考计算机和英语作文,需英语作文网络教育和传统教育的区别
【附源码】Java计算机毕业设计拾穗在线培训考试系统(程序+LW+部署)
Django计算机毕业设计企业培训在线考试系统python(源码程序+lw+远程部署)
2010年4月23日
Windows常见蓝屏故障分析
基于Java (spring-boot)的在线培训考试系统