目录生成小函数代码分析
针对文章内容页,我的博客皮肤使用了一段非常小的函数来生成文章目录。
1. 分析文章内容DOM结构
检查文章内容的DOM结构,可以发现所有的标题都带有类似b3_solo_hxxxx
的id,并且所有的标题均为<h />
标题标签。
因此我们定义标签与其权重之间的关系:
const priorities = {
h1: 5,
h2: 4,
h3: 3,
h4: 2,
h5: 1,
};
2. 获取所有标题节点
使用CSS3 [attribute^=value]
选择器可以很方便的获取到所有标题
const titles = $('[id^=b3_solo_h]');
关于CSS选择器,可以在这里查看更多参考。
3. 对所有标题进行处理
拿到了所有标题之后,我们期望可以根据标题的DOM的tag来进行整理从而生成目录结构。
目标DOM元素结构
h1
h2
h2
h3
h3
h4
h1
h2
h3
h1
希望生成的结构:
h1
h2
h2
h3
h3
h4
h1
h2
h3
h1
h3 # 注意这里,前面是2层缩进
所以我们对之前获取到的$titles
进行遍历。首先获取它的DOM所对应的优先级,之后将当前元素的优先级与上一个元素的优先级进行比较。如果:
- 当前元素的优先级 < 上一个元素的优先级:开始新的缩进并插入一层目录
- 当前元素的优先级 = 上一个元素的优先级:在当前缩进下插入一层目录
- 当前元素的优先级 > 上一个元素的优先级:结束上一个缩进并插入一层目录
我们可以根据逻辑写出代码
let tpl = '<ul class="article__contents">';
let prevPriority; // 上一个元素的优先级
let firstPriority; // 第一个元素的优先级
$titles.each((index, dom) => {
const priority = priorities[dom.tagName.toLowerCase()];
const title = $titles.eq(index).text();
const item = `<li><a href="javascript:void(0);">${title}</a></li>`;
if (!priority) { // 如果没有获取到优先级的则不处理这层目录
return;
}
if (!firstPriority) { // 是第一个目录的情况需要特殊处理
tpl += item;
firstPriority = priority;
} else { // 不是第一个目录的情况,可以按照之前分析的逻辑处理
if (priority === prevPriority) {
tpl += item;
} else if (priority < prevPriority) {
tpl += Array(prevPriority - priority).fill('<ul>').join('') + item;
} else if (priority > prevPriority) {
tpl += Array(priority - prevPriority).fill('</ul>').join('') + item;
}
}
prevPriority = priority;
});
注意这里用了很奇怪的Array(prevPriority - priority).fill('<ul>').join('')
代码,主要是为了处理有的人可能会h1
后面跟h4
,如果只是单纯的使用一个<ul>
的话会导致缩进混乱。
扫尾
使用上述代码,针对上述DOM结构可以生成如下HTML代码片段:
<ul>
<li>h1</li>
<ul>
<li>h2</li>
<li>h2</li>
<ul>
<li>h3</li>
<li>h3</li>
<ul>
<li>h4</li>
</ul>
</ul>
</ul>
<li>h1</li>
<ul>
<li>h2</li>
<ul>
<li>h3</li>
</ul>
</ul>
<li>h1</li>
<ul>
<ul>
<li>h3</li>
我们发现还缺少了最后一级的闭标签。因此需要拿之前设置的prevPriority
和firstPriority
做对比。针对上述情况,prevPriority
为3,firstPriority
为1,因此我们总共需要为当前的目录层级补上2层ul
闭标签。除此之外还需要为整个目录补上1个闭标签。
此逻辑可以通过代码处理:
tpl += Array(firstPriority - prevPriority + 1).fill('</ul>').join('');
至此,我们成功生成了整个目录机构的DOM字符串。
完整代码
因为开发工具的问题真实代码使用了ES3的书写方式,不影响阅读。
function initContents() {
var priorities = {
h1: 5,
h2: 4,
h3: 3,
h4: 2,
h5: 1,
};
var $titles = getArticleTitles(); // 实际上就是 var titles = $('[id^=b3_solo_h]'); getArticleTitles() 这个函数做了一层缓存处理
if ($titles.length === 0) {
$('.J_article__contents').hide();
return;
}
var tpl = '<ul class="article__contents">';
var prevPriority;
var firstPriority;
$titles.each(function(index, dom) {
var priority = priorities[dom.tagName.toLowerCase()];
var title = $titles.eq(index).text();
var item = '<li><a href="javascript:void(0);" data-target="#' + $titles.eq(index).attr('id') + '">' + title + '</a></li>';
if (!priority) {
return;
}
if (!firstPriority) {
tpl += item;
firstPriority = priority;
} else {
if (priority === prevPriority) {
tpl += item;
} else if (priority < prevPriority) {
tpl += Array(prevPriority - priority).fill('<ul>').join('') + item;
} else if (priority > prevPriority) {
tpl += Array(priority - prevPriority).fill('</ul>').join('') + item;
}
}
prevPriority = priority;
});
tpl += Array(firstPriority - prevPriority + 1).fill('</ul>').join('');
$('.J_article__contents--container').append(tpl);
}
大家如果有更好的实现欢迎评论~