应用Markdown编辑编写文章时,咱们会应用h1-h6标签来定义章节题目,但Markdown生成的文章中的h1-h6标签是平行结构的,
并不是一颗树状构造,此时就须要咱们手动去解析这些h标签,并依据他们间接的法则生成一目录树

文章成果

文章dom构造

最终生成的目录构造

思路

  1. 获取文章中所有的h1~h6标签
  2. 比拟h标签的数字,从以后h标签开始判断,如果前面的h标签数字比本人大则当做本人的子孙级,遇到h标签数字比本人小或和本人一样的则立刻进行

如果咱们获取到的h标签是这样的:

var hEles = [      'h4',      'h6',      'h3',      'h4',      'h4',      'h1',      'h2',      'h3',      'h3',      'h3',      'h3',      'h2',      'h3',      'h3'];

则咱们首先须要将其转换成这样:

var arr2 = [     {hLevel: 4}, {hLevel: 6}, {hLevel: 3}, {hLevel: 4},     {hLevel: 4}, {hLevel: 1}, {hLevel: 2}, {hLevel: 3},     {hLevel: 3}, {hLevel: 3}, {hLevel: 3}, {hLevel: 2},     {hLevel: 3}, {hLevel: 3}];

再转换成树状:

var res = [     { hLevel: 4, level: 1, children: [ {hLevel: 6, level: 2} ] },     { hLevel: 3,, level: 1, children: [ {hLevel: 4, level: 2}, {hLevel: 4, level: 2} ] },     {       hLevel: 1,       level: 1,       children: [          {             hLevel: 2,             level: 2             children: [ {hLevel: 3, level: 3}, {hLevel: 3, level: 3}, {hLevel: 3, level: 3}, {hLevel: 3, level: 3} ]          },          {             hLevel: 2,             level: 2,             children: [ {hLevel: 3, level: 3}, {hLevel: 3, level: 3} ]          }       ]     } ];

代码实现

function toTree(flatArr){  var tree = [];  var copyArr = flatArr.map(function (item) { return item; });  // 依据指定级别查找该级别的子孙级,并删除掉曾经查找到的子孙级  var getChildrenByLevel = function (currentLevelItem, arr, level) {    if(!currentLevelItem){      return;    }    // 将level值转成正数,再进行比拟    var minusCurrentLevel = -currentLevelItem.hLevel;    var children = [];    for(var i = 0, len = arr.length; i < len; i++){      var levelItem = arr[i];      if(-levelItem.hLevel < minusCurrentLevel){        children.push(levelItem);      }else { // 只找最近那些子孙级        break;      }    }    // 从数组中删除曾经找到的那些子孙级,免得影响到其余子孙级的查找    if(children.length > 0){      arr.splice(0, children.length);    }    return children;  }  var getTree = function (result, arr, level) {    // 首先将数组第一位移除掉,并增加到后果集中    var currentItem = arr.shift();    currentItem.level = level;    result.push(currentItem);    while (arr.length > 0){      if(!currentItem){        return;      }      // 依据以后级别获取它的子孙级      var children = getChildrenByLevel(currentItem, arr, level);      // 如果以后级别没有子孙级则开始下一个      if(children.length == 0){        currentItem = arr.shift();        currentItem.level = level;        if(currentItem){          result.push(currentItem);        }        continue;      }      currentItem.children = [];      // 查找到的子孙级持续查找子孙级      getTree(currentItem.children, children, level + 1);    }  }  getTree(tree, copyArr, 1);  return tree;}

测试一下:

var arr2 = [     {hLevel: 4}, {hLevel: 6}, {hLevel: 3}, {hLevel: 4},     {hLevel: 4}, {hLevel: 1}, {hLevel: 2}, {hLevel: 3},     {hLevel: 3}, {hLevel: 3}, {hLevel: 3}, {hLevel: 2},     {hLevel: 3}, {hLevel: 3}];console.log(toTree(arr));

接下来只须要依据这个树状构造生成对应的dom树就能够了

// 依据树状构造数据生成章节目录dom树function getChapterDomTree(chapterTreeData, parentNode){  if(!parentNode){    parentNode = createNodeByHtmlStr('<ul class="markdown-toc-list"></ul>')[0];  }  chapterTreeData.forEach(chapterItem => {    var itemDom = createNodeByHtmlStr('<li><a class="toc-level-' + chapterItem.level + '" href="#' + chapterItem.id + '">' + chapterItem.text + '</a></li>')[0];    parentNode.appendChild(itemDom);    if(chapterItem.children){      var catalogList = createNodeByHtmlStr('<ul class="markdown-toc-list"></ul>')[0];      itemDom.appendChild(catalogList);      getChapterDomTree(chapterItem.children, catalogList);    }  });  return parentNode;}// 依据html字符串生成dom元素function createNodeByHtmlStr(htmlStr){  var div = document.createElement('div');  div.innerHTML = htmlStr;  var children = div.children;  div = null;  return children;}var treeData = toTree([...]);var domTree = getChapterDomTree(treeData);