導覽

2017 年 1 月 24 日 tomjoht tomjoht

如果你的 Jekyll 網站有很多頁面,你可能會想要為這些頁面建立導覽。你可以透過程式化取得頁面清單來建立網站導覽,而不需要硬編碼導覽連結。

雖然其他 Jekyll 文件中已有關於 與資料檔案互動 的資訊,但本教學會深入探討如何為你的網站建立更強大的導覽功能。

有兩種主要方式可以在 Jekyll 網站上取得頁面

  • 取得 YAML 資料來源中列出的頁面。將頁面資料儲存在 _data 資料夾中的 YAML(或 JSON 或 CSV)檔案中,迴圈 YAML 屬性,並將值插入你的佈景主題。
  • 透過迴圈頁面前言資料來取得頁面。查看頁面的前言資料以找出特定屬性,傳回這些頁面,並將頁面的前言資料值插入你的佈景主題。

以下範例從基本的導覽情境開始,並加入更精密的元素,以示範傳回頁面的不同方式。在每個情境中,您會看到 3 個元素

  • YAML
  • Liquid
  • 結果

_data 目錄中的 YAML 檔案稱為 samplelist.yml

情境如下

情境 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>

結果

ACME 文件

對於這些虛構範例中的結果,# 手動替換為實際連結值(以避免 404 錯誤)。

當您使用 for 迴圈時,您可以選擇要如何參照您正在迴圈的項目。您選擇的變數(在本例中為 item)會變成您存取清單中每個項目屬性的方式。點狀符號用於取得項目的屬性(例如,item.url)。

YAML 內容有兩種主要格式與此相關

  • 對應
  • 清單

docs_list_title: ACME Documentation 是對應。您可以使用 site.data.samplelist.docs_list_title 存取值。

文件:是一個清單。清單中每個項目都以連字號開頭。與對應不同,您通常不會像對應那樣直接存取清單屬性。如果您想要存取清單中的特定項目,您必須找出您在清單中的位置,並遵循典型的陣列表示法。例如,site.data.samplelist.docs[0]會存取清單中的第一個項目。不過,這很少見。

對於清單,您通常會使用for迴圈來循環清單中的項目,並對每個項目執行某些動作。對於導覽選單,您通常會根據 HTML 主題中使用的導覽結構,將每個清單項目插入li標籤中。

每個連字號 (-) 表示清單中的另一個項目。此範例僅在每個清單項目中具有兩個屬性:標題網址。您可以為每個項目包含任意數量的屬性。清單中每個位置的屬性順序並不重要。

情境 2:已排序清單

假設您想要依據標題對清單進行排序。為此,請將對文件集合的參照轉換為變數,然後將 Liquid 的排序篩選器套用至變數

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 %}

您必須先使用 assigncapture 標籤將 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 %}

結果

在此範例中,Group 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:套用目前頁面的 active 類別

除了將 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 頁面,因為它的 version2

情境 8:根據前置屬性擷取項目

如果您不希望將您的導覽項目儲存在 _data 資料夾中的 YAML 檔中,您可以使用 for 迴圈來查看每個頁面或彙整的開頭資料,並根據開頭資料中的屬性取得內容。

在此情境中,假設我們有一個名為 _docs 的集合。集合通常比頁面好,因為它們允許你縮小要迴圈處理的清單。(盡量避免在大量項目中迴圈處理的情境,因為這會增加你的建置時間。 集合 能幫助你縮小範圍。)

在我們的範例中,docs 集合中有 6 個文件:範例 1、範例 2、主題 1、主題 2、小工具 1 和小工具 2。

集合中的每個文件在 front matter 中至少包含 3 個屬性

  • 標題
  • 類別
  • 順序

為了簡潔起見,每個頁面的 front matter 如下(在此合併)

---
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
---

請注意,即使 類別 用於文件 front matter 中,類別 並不是內建變數,就像用於文章一樣。換句話說,你無法使用 site.docs.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 篩選器依 類別將集合內容分組。更具體地說,group_by 篩選器會將 mydocs 轉換成具有 名稱項目大小 屬性的陣列,如下所示

[
  {"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 陣列中的每個項目,並列印類別 名稱

取得類別名稱後,我們將變數 項目 指定給文件,並使用 排序 篩選器依其 順序 屬性排列文件。點號表示法 cat.items 用於存取 項目 陣列中的內容。 排序 篩選器會依據數字以遞增順序排列項目。

for item in items 迴圈會檢視每個 item,並取得 titleurl 以形成清單項目連結。

有關 group_by 篩選器的更多詳細資料,請參閱 Jekyll 的範本文件,以及 這個 Siteleaf 教學。有關 sort 篩選器的更多詳細資料,請參閱 Liquid 文件中的 sort

不論您是在文件的 front matter 中使用屬性來擷取您的頁面,或使用 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

首先,我們將建立一個包含檔,我們可以使用它來呈現導覽樹狀結構。這個檔案將會是 _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 frontmatter 中擷取它。

{% include nav.html nav=page.nav %}

我們的包含檔會先使用這個,然後檢視每個項目的 subnav 屬性,以遞迴地呈現巢狀清單。

結果