jekyll实现显示文章目录
1. 实现思路
首先先要判断当前页面是否是文章,然后获取文章的各级标题并生成目录,并添加点击跳转到相应标题的功能。完整代码在https://github.com/lxmghct/lxmghct.github.io/blob/master/_includes/table_of_contents.html
2. 实现过程
2.1 获取标题并生成目录
先获取所有的标题h1
-h6
,然后遍历生成目录。生成目录时考虑到有些文章并不是从h1
开始的,有可能h2
或者h3
才是第一级小标题,所以要先获取所有的标题,然后才能判断目录的层级。
为了方便后面的点击跳转,需要给每个标题添加一个id
,如果标题没有id
,则根据标题内容生成一个id
。这样直接把#id
添加到链接的href
属性上就可以实现点击跳转。
// 获取文章内容的标题
const headings = document.querySelectorAll(".post-content h1, post-content h2, .post-content h3, .post-content h4, .post-contenth5, .post-content h6");
if (headings.length === 0) {
document.querySelector(".toc-container").classList.add("hidden");
} else {
document.querySelector(".toc-container").classList.remove("hidden");
}
// 目录容器
const tocList = document.getElementById("toc-list");
const tempTocData = []
// 遍历标题,生成目录
headings.forEach((heading) => {
const level = parseInt(heading.tagName.charAt(1), 10); // 获取标题级别
const listItem = document.createElement("li");
const link = document.createElement("a");
link.textContent = heading.textContent;
if (!heading.id) {
heading.id = heading.textContent.replace(/\s+/g, "-").toLowerCase();
}
link.href = `#${heading.id}`;
listItem.appendChild(link);
tocList.appendChild(listItem);
tempTocData.push({
level: level,
dom: listItem
})
});
2.2 调整目录格式
根据上一步获取的目录层级,设置左边距,使目录结构看起来更加清晰。
const minLevel = Math.min(...tempTocData.map(item => item.level))
tempTocData.forEach(item => {
let diff = item.level - minLevel
item.dom.style.marginLeft = diff * 20 + "px"
if (diff === 0) {
item.dom.classList.add("root-toc")
}
})
2.3 判断文章页并使用目录
将该目录组件封装成了table_of_content.html
组件,然后在文章页引入该组件。判断当前页面是否是文章的方式是判断page.id
或page.date
是否存在。
{% if page.id or page.date %}
{% include table_of_content.html %}
{% endif %}
3. 样式调整
3.1 平滑滚动
点击目录跳转到相应标题时,添加平滑滚动效果。
html, body {
scroll-behavior: smooth;
}
3.2 目录定位方式
我想把目录固定放在页面最左侧,不随滚动条滚动,本来想设置position: fixed
,但是由于我页面的整体布局是有一个header
导航栏,而且导航栏已经设置成了会随滚动条滚动,并非一直固定在页面顶部。所以如果此时目录是fixed
定位,会出现目录和导航栏重叠的情况,而且整体观感也不好。所以我选择了sticky
定位,目的是当导航栏滚动出屏幕前,目录也会随着滚动,当导航栏滚动到屏幕外时,目录会固定在页面最上方。这样既不会和导航栏重叠,浏览起来也比较舒服。
<div class="toc-container">
<div class="toc-header">
<h3>目录导航</h3>
</div>
<ul id="toc-list"></ul>
</div>
.toc-container {
position: sticky;
position: -webkit-sticky; /* 兼容 Safari */
top: 30px;
padding-top: 10px;
}
#toc-list {
height: 70vh;
overflow-y: auto;
list-style: none;
margin: 0;
padding: 0;
}