cheerio制作markDown索引目录
原文链接:Bougie的博客
制作目录索引这种东西当然是放在前端方便。选择放在后端一是为了了解Node后端生态,掌握更多后端技术;二是因为公司实行前后端分离的方式开发,睾贵的JAVA后端经常啥也不做处理就返回一个row数据(甚至有时时间戳都不处理),对此有些无语。
最终目标
- 点击索引单项跳转到相应标题
- 大号标题包含小号标题,小号标题向右缩进
- 滚动页面时自动切换索引项active状态
实现方法
md转化为html
const markDown = require('marked') markDown.setOptions({ headerIds: false, highlight: function(code) { return require('highlight.js').highlightAuto(code).value; }, }) let html = markDown(data.content)
cheerio生成索引
const cheerio = require('cheerio') // decodeEntities防止中文转化为unicdoe const $ = cheerio.load(html,{decodeEntities: false}) // 用hNum生成自定义id let hArr = [], highestLvl, hNum = 0 $('h1, h2, h3, h4, h5, h6').each(function () { let id = `h${hNum}` hNum++ $(this).attr('id', id) let lvl = $(this).get(0).tagName.substr(1) if(!highestLvl) highestLvl = lvl hArr.push({ lvl: lvl - highestLvl + 1, content: $(this).html(), id: id }) }) Object.assign(data, { content: $.html, toc: hArr })
前台展示
if data && data.toc ul#toc-wrapper.toc-wrapper-transform each item in data.toc // 利用lvl判断偏移量 li(class='toc-item text-elli', style=`padding-left: ${item.lvl * 15}px`, id=item.id) a(href=`#${item.id}`, title=item.content).text-elli= item.content
页面滚动过自动切换active
知道getBoundingClientRect API就好做了
function tocToggle() { if($('.article-content').dom.length == 0) return let scrollArr = [] document.querySelectorAll('.article-content h1, h2, h3, h4, h5, h6').forEach(i => { let elTop = Math.abs(i.getBoundingClientRect().top) scrollArr.push({ el: i, top: elTop }) }) if(scrollArr.length == 0) return scrollArr = scrollArr.sort((a, b) => { return a.top - b.top }) let activeId = $(scrollArr[0].el).attr('id') $(`#toc-wrapper #${activeId}`).ac('toc-item-active').siblings().rc('toc-item-active') } $(window).on('scroll', () => { tocToggle() }) tocToggle()
Tips
锚点偏移
本网站的header是fixed在顶部的,锚点不进行偏移会盖住标题。偏移方法:
h1, h2, h3, h4, h5, h6{ &:target{ padding-top: 60px } }