导航
如果你的 Jekyll 网站有很多页面,你可能希望为这些页面创建导航。你可以按编程方式检索页面列表来为你的网站构建导航,而无需对导航链接进行硬编码。
虽然其他 Jekyll 文档中已经提供了有关 与数据文件交互 的信息,但本教程将深入探讨如何为你的网站构建更强大的导航。
有两种主要方法可以在 Jekyll 网站上检索页面
- 检索在 YAML 数据源中列出的页面。将页面数据存储在
_data
文件夹中的 YAML(或 JSON 或 CSV)文件中,循环遍历 YAML 属性,并将值插入到你的主题中。 - 通过循环遍历页面前端信息来检索页面。查看页面的前端信息以识别某些属性,返回这些页面,并将页面的前端信息值插入到你的主题中。
以下示例从一个基本的导航场景开始,并添加更复杂的元素来演示返回页面的不同方法。在每种情况下,你都将看到 3 个元素
- YAML
- Liquid
- 结果
_data
目录中的 YAML 文件称为 samplelist.yml
。
场景如下
- 场景 1:基本列表
- 场景 2:已排序列表
- 场景 3:两级导航列表
- 场景 4:三级导航列表
- 场景 5:使用页面变量选择 YAML 列表
- 场景 6:为当前页面应用活动类
- 场景 7:有条件地包含项目
- 场景 8:基于前端信息属性检索项目
- 场景 9:使用递归进行嵌套树导航
场景 1:基本列表
您希望返回一个基本页面列表。
YAML
docs_list_title: ACME Documentation
docs:
- title: Introduction
url: introduction.html
- title: Configuration
url: configuration.html
- title: Deployment
url: deployment.html
Liquid
<h2>{{ site.data.samplelist.docs_list_title }}</h2>
<ul>
{% for item in site.data.samplelist.docs %}
<li><a href="{{ item.url }}">{{ item.title }}</a></li>
{% endfor %}
</ul>
结果
对于这些虚构示例中的结果,#
被手动替换为实际链接值(以避免 404 错误)。
当您使用 for
循环时,您可以选择如何引用您循环遍历的项目。您选择的变量(在本例中为 item
)将成为您访问列表中每个项目属性的方式。点表示法用于获取项目的属性(例如,item.url
)。
YAML 内容有两种主要格式与这里相关
- 映射
- 列表
docs_list_title: ACME Documentation
是一个映射。您可以使用 site.data.samplelist.docs_list_title
访问该值。
docs:
是一个列表。该列表以连字符开头。与映射不同,您通常不会像使用映射那样直接访问列表属性。如果您想访问列表中的特定项目,您必须识别您想要的列表中的位置,遵循典型的数组表示法。例如,site.data.samplelist.docs[0]
将访问列表中的第一个项目。但是,这很少会这样做。
对于列表,您通常使用 for
循环来循环遍历项目列表,并对每个项目执行某些操作。对于导航菜单,您通常会根据 HTML 主题中使用的导航结构将每个列表项插入到 li
标记中。
每个连字符 (-
) 表示列表中的另一个项目。此示例仅在每个列表项中具有两个属性:title
和 url
。您可以为每个项目包含任意数量的属性。列表中每个位置的属性顺序无关紧要。
场景 2:已排序列表
假设您想按 title
对列表进行排序。为此,将对 docs
集合的引用转换为变量,然后将 Liquid 的 sort
过滤器应用于该变量
Liquid
{% assign doclist = site.data.samplelist.docs | sort: 'title' %}
<ol>
{% for item in doclist %}
<li><a href="{{ item.url }}">{{ item.title }}</a></li>
{% endfor %}
</ol>
结果
项目现在按字母顺序显示。sort
属性在 Liquid 过滤器中应用于 title
,这是列表中的实际属性。如果 title
不是属性,我们需要按其他属性排序。
请参阅 Liquid 数组过滤器 了解更多过滤器选项。请注意,您不能简单地使用此语法
{% for item in site.data.samplelist.docs | sort: "title" %}{% endfor %}
您必须先使用 assign
或 capture
标记将 site.data.samplelist.docs
转换为变量。
场景 3:两级导航列表
假设您想要一个更强大的列表,其中包含标题和子项的多个部分。为此,请为每个列表项添加一个附加级别来存储此信息
YAML
toc:
- title: Group 1
subfolderitems:
- page: Thing 1
url: /thing1.html
- page: Thing 2
url: /thing2.html
- page: Thing 3
url: /thing3.html
- title: Group 2
subfolderitems:
- page: Piece 1
url: /piece1.html
- page: Piece 2
url: /piece2.html
- page: Piece 3
url: /piece3.html
- title: Group 3
subfolderitems:
- page: Widget 1
url: /widget1.html
- page: Widget 2
url: /widget2.html
- page: Widget 3
url: /widget3.html
Liquid
{% for item in site.data.samplelist.toc %}
<h3>{{ item.title }}</h3>
<ul>
{% for entry in item.subfolderitems %}
<li><a href="{{ entry.url }}">{{ entry.page }}</a></li>
{% endfor %}
</ul>
{% endfor %}
结果
在此示例中,组 1
是第一个列表项。在该列表项中,其子页面被包含为一个属性,该属性本身包含一个列表 (subfolderitems
)。
Liquid 代码通过 for item in site.data.samplelist.toc
查看第一层,然后通过 for entry in item.subfolderitems
查看第二层属性。就像 item
是我们循环遍历的项目的任意名称一样,entry
也是如此。
场景 4:三级导航列表
在上一部分的基础上,让我们为列表添加一个更深的级别 (subsubfolderitems
)。这里的格式会变得更加复杂,但原理是一样的。
YAML
toc2:
- title: Group 1
subfolderitems:
- page: Thing 1
url: /thing1.html
- page: Thing 2
url: /thing2.html
subsubfolderitems:
- page: Subthing 1
url: /subthing1.html
- page: Subthing 2
url: /subthing2.html
- page: Thing 3
url: /thing3.html
- title: Group 2
subfolderitems:
- page: Piece 1
url: /piece1.html
- page: Piece 2
url: /piece2.html
- page: Piece 3
url: /piece3.html
subsubfolderitems:
- page: Subpiece 1
url: /subpiece1.html
- page: Subpiece2
url: /subpiece2.html
- title: Group 3
subfolderitems:
- page: Widget 1
url: /widget1.html
subsubfolderitems:
- page: Subwidget 1
url: /subwidget1.html
- page: Subwidget 2
url: /subwidget2.html
- page: Widget 2
url: /widget2.html
- page: Widget 3
url: /widget3.html
Liquid
<div>
{% if site.data.samplelist.toc2[0] %}
{% for item in site.data.samplelist.toc2 %}
<h3>{{ item.title }}</h3>
{% if item.subfolderitems[0] %}
<ul>
{% for entry in item.subfolderitems %}
<li><a href="{{ entry.url }}">{{ entry.page }}</a>
{% if entry.subsubfolderitems[0] %}
<ul>
{% for subentry in entry.subsubfolderitems %}
<li><a href="{{ subentry.url }}">{{ subentry.page }}</a></li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% endfor %}
{% endif %}
</div>
结果
在此示例中,if site.data.samplelist.toc2[0]
用于确保 YAML 级别实际上包含项目。如果没有内容位于 [0]
位置,我们可以跳过在此级别中查找。
专业提示:对齐 for
循环和 if
语句
为了保持代码清晰,请对齐开始和结束的 Liquid 标签,例如 for
循环和 if
语句。这样,您就知道何时关闭了打开的标签。如果代码将出现在 Markdown 页面中,请将 HTML 开始和结束标签与左边缘对齐,以便 Markdown 过滤器不会将内容视为代码示例。如有必要,您可以将整个代码示例包装在 div
标签中,以确保代码具有将代码括起来的 HTML 标签。
场景 5:使用页面变量选择 YAML 列表
假设您的侧边栏会根据不同的文档集而有所不同。您的网站上可能有 3 种不同的产品,因此您需要 3 个不同的侧边栏——每个侧边栏都针对该产品而设计。
您可以将侧边栏列表的名称存储在页面前置内容中,然后将该值动态地传递到列表中。
页面前置内容
---
title: My page
sidebar: toc
---
Liquid
<ul>
{% for item in site.data.samplelist[page.sidebar] %}
<li><a href="{{ item.url }}">{{ item.title }}</a></li>
{% endfor %}
</ul>
结果
在此场景中,我们希望将页面前置内容中的值传递到包含变量的 for
循环中。当分配的变量不是字符串而是数据引用时,您必须使用方括号(而不是大括号)来引用前置内容的值。
有关更多信息,请参阅 Liquid 文档中的 表达式和变量。在无法使用点表示法的地方使用方括号。您还可以在此 Stack Overflow 回答 中阅读更多详细信息。
场景 6:为当前页面应用活动类
除了将 YAML 数据文件中的项目插入到您的列表中之外,您通常还希望在用户查看该页面时突出显示当前链接。您可以通过为与当前页面 URL 匹配的项目插入 active
类来实现此目的。
CSS
.result li.active a {
color: lightgray;
cursor: default;
}
Liquid
{% for item in site.data.samplelist.docs %}
<li class="{% if item.url == page.url %}active{% endif %}">
<a href="{{ item.url }}">{{ item.title }}</a>
</li>
{% endfor %}
结果
在这种情况下,假设 Deployment
是当前页面。
为了确保 item.url
(存储在 YAML 文件中)与 page.url
匹配,将 {{ page.url }}
打印到页面中会很有帮助。
场景 7:有条件地包含项目
您可能希望有条件地将项目包含在您的列表中。例如,您可能有多个网站输出,并且只想为某些输出包含侧边栏项目。您可以在每个列表项中添加属性,然后使用这些属性有条件地包含内容。
YAML
docs2_list_title: ACME Documentation
docs2:
- title: Introduction
url: introduction.html
version: 1
- title: Configuration
url: configuration.html
version: 1
- title: Deployment
url: deployment.html
version: 2
Liquid
<ul>
{% for item in site.data.samplelist.docs2 %}
{% if item.version == 1 %}
<li><a href="{{ item.url }}">{{ item.title }}</a></li>
{% endif %}
{% endfor %}
</ul>
结果
Deployment
页面被排除在外,因为其 version
为 2
。
场景 8:基于前端信息属性检索项目
如果您不想将导航项目存储在 _data
文件夹中的 YAML 文件中,则可以使用 for
循环来查看每个页面或集合的前置内容,并根据前置内容中的属性获取内容。
在此场景中,假设我们有一个名为 _docs
的集合。集合通常比页面更好,因为它们允许您缩小循环范围。(尽量避免循环处理大量项目的情况,因为这会增加您的构建时间。 集合 帮助您缩小范围。)
在我们的场景中,docs
集合中有 6 个文档:示例 1、示例 2、主题 1、主题 2、小部件 1 和小部件 2。
集合中的每个文档都包含正文中的至少 3 个属性
标题
类别
顺序
每个页面的正文如下(在此处合并以简化内容)
---
Title: Sample 1
category: getting-started
order: 1
---
---
Title: Sample 2
category: getting-started
order: 2
---
---
Title: Topic 1
category: configuration
order: 1
---
---
Title: Topic 2
category: configuration
order: 2
---
---
Title: Widget 1
category: deployment
order: 1
---
---
Title: Widget 2
category: deployment
order: 2
---
请注意,即使正文中使用了 category
,category
也不是像文章中那样的内置变量。换句话说,您不能使用 site.docs.category
直接查看 category
的内部内容。
如果您只想获取特定类别中的集合中的所有文档,可以使用 for
循环和 if
条件来检查特定类别
<h3>Getting Started</h3>
<ul>
{% for doc in site.docs %}
{% if doc.category == "getting-started" %}
<li><a href="{{ doc.url }}">{{ doc.title }}</a></li>
{% endif %}
{% endfor %}
</ul>
结果如下
如果您正在设置一个知识库,并且每个类别中都有几十个主题,每个类别都在其自己的页面上显示,这可能很有用。
但是,假设您想按类别对项目进行排序,并将它们分组在类别名称下,而无需对类别名称进行硬编码。为此,您可以使用两个过滤器
group_by
sort
以下是获取按相应类别标题分组的页面列表的代码
Liquid
{% assign mydocs = site.docs | group_by: 'category' %}
{% for cat in mydocs %}
<h2>{{ cat.name | capitalize }}</h2>
<ul>
{% assign items = cat.items | sort: 'order' %}
{% for item in items %}
<li><a href="{{ item.url }}">{{ item.title }}</a></li>
{% endfor %}
</ul>
{% endfor %}
结果
让我们逐步了解代码。首先,我们将一个变量 (mydocs
) 分配给集合内容 (site.docs
)。
group_by
过滤器按 category
对集合内容进行分组。更具体地说,group_by
过滤器将 mydocs
转换为一个数组,其中包含 name
、items
和 size
属性,如下所示
[
{"name": "getting-started", "items": [Sample 1, Sample 2],"size": 2},
{"name": "configuration", "items": [Topic 1, Topic 2], "size": 2},
{"name": "deployment", "items": [Widget 1, Widget 2], "size": 2}
]
使用 for cat in mydocs
,我们查看 mydocs
数组中的每个项目,并打印类别 name
。
获取类别名称后,我们为文档分配变量 items
,并使用 sort
过滤器按 order
属性对文档进行排序。点符号 cat.items
用于访问 items
数组中的内容。 sort
过滤器按数字升序对项目进行排序。
for item in items
循环查看每个 item
,并获取 title
和 url
以形成列表项链接。
有关 group_by
过滤器的更多详细信息,请参阅 Jekyll 模板文档 以及 此 Siteleaf 教程。有关 sort
过滤器的更多详细信息,请参阅 Liquid 文档中的 sort。
无论您是在文档的前端内容中使用属性来检索页面还是使用 YAML 数据文件,在这两种情况下,您都可以通过编程方式为您的网站构建更强大的导航。
场景 9:使用递归进行嵌套树导航
假设您想要任意深度的嵌套树导航。我们可以通过递归循环遍历我们的导航链接树来实现这一点。
YAML
nav:
- title: Deployment
url: deployment.html
subnav:
- title: Heroku
url: heroku.html
subnav:
- title: Jekyll on Heroku
url: jekyll-on-heroku.html
- title: Help
url: help.html
Liquid
首先,我们将创建一个可用于呈现导航树的 include。此文件将为 _includes/nav.html
<ul>
{% for item in include.nav %}
<li><a href="{{ item.url }}">{{ item.title }}</a>
{% if item.subnav %}
{% include nav.html nav=item.subnav %}
{% endif %}
</li>
{% endfor %}
</ul>
要在您的布局或页面中呈现此内容,您只需包含模板并传入 nav
参数。在这种情况下,我们将使用 page.nav
从 yaml 前端内容中获取它。
{% include nav.html nav=page.nav %}
我们的 include 将首先使用此内容,然后查看每个项目的 subnav
属性以递归呈现嵌套列表。
结果