为文章添加 Table of contents (TOC)
Toc 其实是 Table of Contents 的缩写,中文翻译就是目录,方便读者能快速找到感兴趣的段落阅读。这篇文章是如何在不修改 Ghost 主题的前提下,用 Code Injection 实现文章 TOC 的效果。
在《溫故知新 2024 EP09》中的 imMS企划中就写到:
在近期一些文章中加入 TOC,但是在大屏幕的并没有实现侧边栏的功能,还需要多研究,虽然还是有现成的方案,但是我还是觉得做点研究才比较好。
🫙什么是 TOC?
Toc 其实是 Table of Contents 的缩写,中文翻译就是目录,方便读者能快速找到感兴趣的段落阅读。这篇文章是如何在不修改 Ghost 主题的前提下,用 Code Injection 实现文章 TOC 的效果。
Ghost 官方为文章添加 TOC 教程,并不适合所有人
最后最为简单的方法就是在文章的 Header 以及 Footer 中注入代码(code injection),只不过 Ghost 官方教程中需要在主题中的 default.hbs
中加入 Tocbot 的脚本,虽然看似简单但是我的部署环境但由于部署环境的差异,又或是有部分没有上传权限的用户就没有办法透过这个方法实现。
🙌 所幸网络上关于 Ghost TOC 教程还不少
作者 Ran Ding 在「How to add a table of contents in Ghost without editing the site template」中分享如何不修改主题的情况下在文章的 Code Injection,分别在 Post Header 以及 Post Footer 粘贴脚本 ,但如果打算做到所有文章都有 TOC 的话就在 设定 > Code injection
中加入即可。 有一说一, Ran Ding 在脚本中也将备注写了进去,让我们对这个 code 多了写了解,也能动手更改成自己想要的样子(虽然不太可能会做)。
Code Injection:
header
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.12.3/tocbot.css">
<style>
.gh-content {
position: relative;
}
.gh-toc > .toc-list {
position: relative;
}
.toc-list {
overflow: hidden;
list-style: none;
}
@media (min-width: 1300px) {
.gh-sidebar {
position: absolute;
top: 0;
bottom: 0;
margin-top: 4vmin;
margin-left: 20px;
grid-column: wide-end / main-end; /* Place the TOC to the right of the content */
width: inline-block;
white-space: nowrap;
}
.gh-toc-container {
position: sticky; /* On larger screens, TOC will stay in the same spot on the page */
top: 4vmin;
}
}
.gh-toc .is-active-link::before {
background-color: var(--ghost-accent-color); /* Defines TOC accent color based on Accent color set in Ghost Admin */
}
</style>
Footer
<script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.12.3/tocbot.min.js"></script>
<script>
const parent = document.querySelector(".gh-content.gh-canvas");
// Create the <aside> element
const asideElement = document.createElement("aside");
asideElement.setAttribute("class", "gh-sidebar");
//asideElement.style.zIndex = 0; // sent to back so it doesn't show on top of images
// Create the container div for title and TOC
const containerElement = document.createElement("div");
containerElement.setAttribute("class", "gh-toc-container");
// Create the title element
const titleElement = document.createElement("div");
titleElement.textContent = "Table of Contents";
titleElement.style.fontWeight = "bold";
containerElement.appendChild(titleElement);
// Create the <div> element for TOC
const divElement = document.createElement("div");
divElement.setAttribute("class", "gh-toc");
containerElement.appendChild(divElement);
// Append the <div> element to the <aside> element
asideElement.appendChild(containerElement);
parent.insertBefore(asideElement, parent.firstChild);
tocbot.init({
// Where to render the table of contents.
tocSelector: '.gh-toc',
// Where to grab the headings to build the table of contents.
contentSelector: '.gh-content',
// Which headings to grab inside of the contentSelector element.
headingSelector: 'h1, h2, h3, h4',
// Ensure correct positioning
hasInnerContainers: true,
});
// Get the table of contents element
const toc = document.querySelector(".gh-toc");
const sidebar = document.querySelector(".gh-sidebar");
// Check the number of items in the table of contents
const tocItems = toc.querySelectorAll('li').length;
// Only show the table of contents if it has more than 5 items
if (tocItems > 2) {
sidebar.style.display = 'block';
} else {
sidebar.style.display = 'none';
}
</script>
🐛好像有一些错误以及小问题
Ok, 因为是 2023 年的文章,那时的 Tocbot 库还停留在 4.12.3
,可是执笔的当下 Tocbot 库已经更新到 4.32.2
了,不过我看 Tocbot 只用到 4.30.0
所以我只用到 4.30.0
,稳定比较重要。
其实我已经是很满意了这个效果了,唯一观察道德缺点是当有图片是全局或是宽版的话,toc 会被该图片给遮挡,应该是因为先载入header的code 的关系吧。而且如果文章中用的是/header
的话,也是会很奇怪,所幸我的文章很少用到 /header
。
🏃DEMO Toc 的效果
这里演示的文章是 Hetzner VPS + Docker 部署心得效果看起来不错。
总结
这是我所用过最简单的加入 Toc 的方法,而且小问题真的是小问题,因为这个教程已经涵盖了手机上阅读文章的 TOC 的解决方案,只要在 Code Injection 注入脚本即可(记得更换 Tocbot 版本)。 我目前打算在接下来的文章中添加 Tocbot 脚本,如果一切顺利做全站代码注入,像是我之前的 如何使用 Prism CDN 做到程式碼高亮 Syntax highlighting 一样。或许将来当可以修改主题的时候配合 Github Action,让这些代码只在 Post.hbs 实现。 最后我还是很感谢 Ran Ding 的这篇教程,除了这篇之外 RanDing 也有不少 Ghost 主题相关的教程,可以去看看,说不定将来我也能独当一面,为 Ghost 社群作贡献。