如何给 Ghost 博客添加目录?图文教程
为了给文章添加目录,我们将使用一个名为 Tocbot 的 JavaScript (JS) 库。该库为我们解决了复杂的工作,其中包括查找帖子的二级、三级等标题,并为此创建文章的锚点链接以及向读者显示它们在页面上的位置。
为了给文章添加目录,我们将使用一个名为 Tocbot 的 JavaScript (JS) 库。该库为我们解决了复杂的工作,其中包括查找帖子的二级、三级等标题,并为此创建文章的锚点链接以及向读者显示它们在页面上的位置。
Tocbot 的工作原理是查看帖子的标题标签,例如 <h2>
,通过选择标题文本并单击弹出编辑器中的 H
图标,在 Ghost 中创建标题。Tocbot 库由两部分组成:
- 处理功能的 JS 文件
- 处理基本样式的 CSS 文件
两者都需要加载到你的 Ghost 主题中。在本教程中,我们将使用 Ghost 的默认主题 Casper。虽然这些步骤适用于任何 Ghost 主题,但你需要修改代码才能适用于你的其它特定主题。
1.编辑default.hbs
文件
在代码编辑器中打开 default.hbs
,在此文件中,添加 Tocbot 脚本和样式,如下所述。
添加 CSS
在 default.hbs
文件顶部附近的 head
标记中的 {{ghost_head}}
之前,添加 Tocbot CSS:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.12.3/tocbot.css">
添加 style
样式
同样的,上面的 CSS 链接添加后,在其后面,接着添加下面的 CSS 样式,下面的样式也是包裹在<head></head>
标签里面的,不要放错地方。
<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;
grid-column: wide-start / main-start; /* Place the TOC to the left of the content */
}
.gh-toc {
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>
上面的 CSS 的意思是,让目录在大屏幕上与文章内容一起显示,然后在小屏幕上显示在文章内容上方。
添加 JS
在 default.hbs
中,在靠近文件末尾的 {{ghost_foot}}
之前,添加 Tocbot 脚本,图见下文:
<script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.12.3/tocbot.min.js"></script>
初始化 Tocbot 脚本
加载 Tocbot 脚本后,我们现在需要对其进行初始化,这会告诉 Tocbot 我们想要在页面上的哪个位置放置目录以及要添加到其中的内容。引入上面的 JS 链接后,在其后面,添加下面的脚本:
{{! Initialize Tocbot after you load the script }}
<script>
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,
});
</script>
本教程中使用的类(gh-content
、gh-toc
)基于 Casper 主题。你需要更改gh-content
以匹配主题的容器类(包含你的帖子内容)。gh-toc
可以是你想要的任何内容,你只需确保初始化脚本中的类与模板中的类匹配。
2.编辑 post.hbs
文件
让我们定义我们希望目录在主题中显示的位置。在 post.hbs
中,在 {{content}}
帮助器之前添加以下代码片段:
<aside class="gh-sidebar"><div class="gh-toc"></div></aside> {{! The TOC will be inserted here }}
这段代码的作用就是让目录附着在文章的左上方,跟上面添加的 CSS 文件相对应。
3.完整代码
作为最终检查,你的 default.hbs
和 post.hbs
文件应如下所示:
default.hbs:
<!DOCTYPE html>
<html lang="{{@site.locale}}"{{#match @custom.color_scheme "Dark"}} class="dark-mode"{{else match @custom.color_scheme "Auto"}} class="auto-color"{{/match}}>
<head>
{{!-- Basic meta - advanced meta is output with {ghost_head} below --}}
<title>{{meta_title}}</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="HandheldFriendly" content="True" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{{!-- Theme assets - use the {asset} helper to reference styles & scripts,
this will take care of caching and cache-busting automatically --}}
<link rel="stylesheet" type="text/css" href="{{asset "built/screen.css"}}" />
{{!-- TOC styles --}}
<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;
grid-column: wide-start / main-start; /* Place the TOC to the left of the content */
}
.gh-toc {
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>
{{!-- This tag outputs all your advanced SEO meta, structured data, and other important settings,
it should always be the last tag before the closing head tag --}}
{{ghost_head}}
</head>
<body class="{{body_class}}{{#match @custom.title_font "=" "Elegant serif"}} has-serif-title{{/match}}{{#match @custom.body_font "=" "Modern sans-serif"}} has-sans-body{{/match}}{{#if @custom.show_publication_cover}} has-cover{{/if}}{{#is "home"}}{{#unless @custom.show_logo_in_navigation}} no-logo{{/unless}}{{/is}}">
<div class="viewport">
<header id="gh-head" class="gh-head outer">
<nav class="gh-head-inner inner">
<div class="gh-head-brand">
<a class="gh-head-logo{{#unless @site.logo}} no-image{{/unless}}" href="{{@site.url}}">
{{#if @site.logo}}
<img src="{{@site.logo}}" alt="{{@site.title}}" />
{{else}}
{{@site.title}}
{{/if}}
</a>
<a class="gh-burger" role="button">
<div class="gh-burger-box">
<div class="gh-burger-inner"></div>
</div>
</a>
</div>
<div class="gh-head-menu">
{{navigation}}
</div>
<div class="gh-head-actions">
<div class="gh-social">
{{#if @site.facebook}}
<a class="gh-social-link gh-social-facebook" href="{{facebook_url @site.facebook}}" title="Facebook" target="_blank" rel="noopener">{{> "icons/facebook"}}</a>
{{/if}}
{{#if @site.twitter}}
<a class="gh-social-link gh-social-twitter" href="{{twitter_url @site.twitter}}" title="Twitter" target="_blank" rel="noopener">{{> "icons/twitter"}}</a>
{{/if}}
</div>
{{#if @site.members_enabled}}
{{#unless @member}}
<a class="gh-head-button" href="#/portal/signup" data-portal="signup">Subscribe</a>
{{else}}
<a class="gh-head-button" href="#/portal/account" data-portal="account">Account</a>
{{/unless}}
{{/if}}
</div>
</nav>
</header>
<div class="site-content">
{{!-- All other templates get inserted here, index.hbs, post.hbs, etc --}}
{{{body}}}
</div>
{{!-- The global footer at the very bottom of the screen --}}
<footer class="site-footer outer">
<div class="inner">
<section class="copyright"><a href="{{@site.url}}">{{@site.title}}</a> © {{date format="YYYY"}}</section>
<nav class="site-footer-nav">
{{navigation type="secondary"}}
</nav>
<div><a href="https://ghost.org/" target="_blank" rel="noopener">Powered by Ghost</a></div>
</div>
</footer>
</div>
{{!-- /.viewport --}}
{{!-- Scripts - handle member signups, responsive videos, infinite scroll, floating headers, and galleries --}}
<script
src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
crossorigin="anonymous">
</script>
<script src="{{asset "built/casper.js"}}"></script>
<script>
$(document).ready(function () {
// Mobile Menu Trigger
$('.gh-burger').click(function () {
$('body').toggleClass('gh-head-open');
});
// FitVids - Makes video embeds responsive
$(".gh-content").fitVids();
});
</script>
{{!-- Tocbot script --}}
<script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.12.3/tocbot.min.js"></script>
{{! Initialize Tocbot after you load the script }}
<script>
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,
});
</script>
{{!-- Ghost outputs required functional scripts with this tag - it should always be the last thing before the closing body tag --}}
{{ghost_foot}}
</body>
</html>
post.hbs:
{{!< default}}
{{!-- The tag above means: insert everything in this file
into the {body} tag of the default.hbs template --}}
{{#post}}
{{!-- Everything inside the #post block pulls data from the post --}}
<main id="site-main" class="site-main">
<article class="article {{post_class}} {{#match @custom.post_image_style "Full"}}image-full{{else match @custom.post_image_style "=" "Small"}}image-small{{/match}}">
<header class="article-header gh-canvas">
<div class="article-tag post-card-tags">
{{#primary_tag}}
<span class="post-card-primary-tag">
<a href="{{url}}">{{name}}</a>
</span>
{{/primary_tag}}
{{#if featured}}
<span class="post-card-featured">{{> "icons/fire"}} Featured</span>
{{/if}}
</div>
<h1 class="article-title">{{title}}</h1>
{{#if custom_excerpt}}
<p class="article-excerpt">{{custom_excerpt}}</p>
{{/if}}
<div class="article-byline">
<section class="article-byline-content">
<ul class="author-list">
{{#foreach authors}}
<li class="author-list-item">
{{#if profile_image}}
<a href="{{url}}" class="author-avatar">
<img class="author-profile-image" src="{{img_url profile_image size="xs"}}" alt="{{name}}" />
</a>
{{else}}
<a href="{{url}}" class="author-avatar author-profile-image">{{> "icons/avatar"}}</a>
{{/if}}
</li>
{{/foreach}}
</ul>
<div class="article-byline-meta">
<h4 class="author-name">{{authors}}</h4>
<div class="byline-meta-content">
<time class="byline-meta-date" datetime="{{date format="YYYY-MM-DD"}}">{{date}}</time>
{{#if reading_time}}
<span class="byline-reading-time"><span class="bull">•</span> {{reading_time}}</span>
{{/if}}
</div>
</div>
</section>
</div>
{{#match @custom.post_image_style "!=" "Hidden"}}
{{#if feature_image}}
<figure class="article-image">
{{!-- This is a responsive image, it loads different sizes depending on device
https://medium.freecodecamp.org/a-guide-to-responsive-images-with-ready-to-use-templates-c400bd65c433 --}}
<img
srcset="{{img_url feature_image size="s"}} 300w,
{{img_url feature_image size="m"}} 600w,
{{img_url feature_image size="l"}} 1000w,
{{img_url feature_image size="xl"}} 2000w"
sizes="(min-width: 1400px) 1400px, 92vw"
src="{{img_url feature_image size="xl"}}"
alt="{{#if feature_image_alt}}{{feature_image_alt}}{{else}}{{title}}{{/if}}"
/>
{{#if feature_image_caption}}
<figcaption>{{feature_image_caption}}</figcaption>
{{/if}}
</figure>
{{/if}}
{{/match}}
</header>
<section class="gh-content gh-canvas">
<
<aside class="gh-sidebar"><div class="gh-toc"></div></aside> {{! The TOC will be inserted here }}
{{content}}
</section>
{{!--
<section class="article-comments gh-canvas">
If you want to embed comments, this is a good place to paste your code!
</section>
--}}
</article>
</main>
{{!-- A signup call to action is displayed here, unless viewed as a logged-in member --}}
{{#if @site.members_enabled}}
{{#unless @member}}
{{#if access}}
<section class="footer-cta outer">
<div class="inner">
{{#if @custom.email_signup_text}}<h2 class="footer-cta-title">{{@custom.email_signup_text}}</h2>{{/if}}
<a class="footer-cta-button" href="#/portal" data-portal>
<div class="footer-cta-input">Enter your email</div>
<span>Subscribe</span>
</a>
{{!-- ^ This looks like a form element, but it's just a link to Portal,
making the form validation and submission much simpler. --}}
</div>
</section>
{{/if}}
{{/unless}}
{{/if}}
{{!-- Read more links, just above the footer --}}
{{#if @custom.show_recent_posts_footer}}
{{!-- The {#get} helper below fetches some of the latest posts here
so that people have something else to read when they finish this one.
This query gets the latest 3 posts on the site, but adds a filter to
exclude the post we're currently on from being included. --}}
{{#get "posts" filter="id:-{{id}}" limit="3" as |more_posts|}}
{{#if more_posts}}
<aside class="read-more-wrap outer">
<div class="read-more inner">
{{#foreach more_posts}}
{{> "post-card"}}
{{/foreach}}
</div>
</aside>
{{/if}}
{{/get}}
{{/if}}
{{/post}}
最终,你的页面看起来大概是这样子的:
总结
为了添加文章目录,使用名为 Tocbot 的 JavaScript(JS)库。该库简化了复杂的工作,通过查找帖子的标题标签(如<h2>)创建目录锚点链接,方便读者查看位置。Tocbot 包含处理功能的 JS 文件和处理基本样式的 CSS 文件,需要加载到 Ghost 主题中。在教程中,以 Ghost 默认主题 Casper 为例,修改 default.hbs 文件添加 Tocbot 脚本和样式,同时在 post.hbs 中定义目录位置。
知识扩展: