使用Markdown编辑编写文章时,我们会使用h1-h6
标签来定义章节标题,但Markdown生成的文章中的h1-h6
标签是平行结构的,
并不是一颗树状结构,此时就需要我们手动去解析这些h标签,并根据他们直接的规律生成一目录树
思路
- 获取文章中所有的h1~h6标签
- 比较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('
')[0];
}
chapterTreeData.forEach(chapterItem => {
var itemDom = createNodeByHtmlStr('' + chapterItem.text + ' ')[0];
parentNode.appendChild(itemDom);
if(chapterItem.children){
var catalogList = createNodeByHtmlStr('
')[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);