给marked增加TOC(Table of content)

来源:互联网 发布:linux服务器维护教程 编辑:程序博客网 时间:2024/06/06 20:55

一直使用marked库解析Markdown,现在想给它增加TOC功能
使用marked解析markdown文本是很简单的,如:marked(markdownText)就可以得到解析后的html内容

初始化marked

引入库

var marked = require('marked');

重写renderer.heading

tocObj后面再介绍,返回的header中包含了用于定位的锚点。当然你也可以直接将锚点写在h标签中,我这里单独用一个a标签是为了解决,网站有固定的导航头的时候,定位锚点的时候标题被遮盖的问题,如:点击带锚点的网址后,如何让网页位置向下偏移一小段距离

var renderer = new marked.Renderer();renderer.heading = function(text, level, raw) {  var anchor = tocObj.add(text, level);  return `<a id=${anchor} class="anchor-fix"></a><h${level}>${text}</h${level}>\n`;};

设置参数

highlight是我用来处理代码高亮的,这里不考虑。

marked.setOptions({    renderer: renderer,    highlight: function(code) {      return require("highlight.js").highlightAuto(code).value;    },});

保存marked解析后的header信息

在renderer.heading使用tocObj保存h标签的text和level并且返回一个生产的锚点,text即标签的内容,level是几级标题,如:h1,h2,h3等

产生toc的html代码

有了header信息就可以生成toc了,toc是根据h标签的等级层次来生成的,跟目录树是一样的,并不是一个单纯的列表平铺下来的。

我们目前拿到的数据是这样的,数字是level,标题是text,它们按顺序存放在数组中

h1一级标题h1一级标题  h2二级标题  h2二级标题    h3三级标题  h2二级标题h1一级标题

现在要把这些数据转换成html标签的形式

用ul和li标签来表示,就存在ul中嵌套ul的情况,如下:

<ul>  <li>一级标题</li>  <li>一级标题</li>  <ul>    <li>二级标题</li>    <li>二级标题</li>    <ul>      <li>三级标题</li>    </ul>    <li>二级标题</li>  </ul>  <li>一级标题</li></ul>

转换代码

const tocObj = {   add: function(text, level) {    var anchor = `#toc${level}${++this.index}`;    this.toc.push({ anchor: anchor, level: level, text: text });    return anchor;  },  // 使用堆栈的方式处理嵌套的ul,li,level即ul的嵌套层次,1是最外层  // <ul>  //   <li></li>  //   <ul>  //     <li></li>  //   </ul>  //   <li></li>  // </ul>  toHTML: function() {    let levelStack = [];    let result = '';    const addStartUL = () => { result += '<ul>'; };    const addEndUL = () => { result += '</ul>\n'; };    const addLI = (anchor, text) => { result += '<li><a href="#'+anchor+'">'+text+'<a></li>\n'; };    this.toc.forEach(function (item) {      let levelIndex = levelStack.indexOf(item.level);      // 没有找到相应level的ul标签,则将li放入新增的ul中      if (levelIndex === -1) {        levelStack.unshift(item.level);        addStartUL();        addLI(item.anchor, item.text);      } // 找到了相应level的ul标签,并且在栈顶的位置则直接将li放在此ul下      else if (levelIndex === 0) {        addLI(item.anchor, item.text);      } // 找到了相应level的ul标签,但是不在栈顶位置,需要将之前的所有level出栈并且打上闭合标签,最后新增li      else {        while (levelIndex--) {          levelStack.shift();          addEndUL();        }        addLI(item.anchor, item.text);      }    });    // 如果栈中还有level,全部出栈打上闭合标签    while(levelStack.length) {      levelStack.shift();      addEndUL();    }    // 清理先前数据供下次使用    this.toc = [];    this.index = 0;    return result;  },  toc: [],   index: 0};

最后的代码实现

post表示文章对象,content是markdown内容,调用marked的时候renderer.heading中就已经往tocObj中写入文章的标题信息了,最后只需要调用一下toHTML就可以产生toc的html代码了。

post2html: function(post) {  if (post) {    post.content = marked(post.content);    post.toc = tocObj.toHTML();  }}

锚点定位偏移css

20px是被导航条遮住的高度

.anchor-fix {    display: block;    height: 20px; /*same height as header*/    margin-top: -20px; /*same height as header*/    visibility: hidden;}
原创粉丝点击