ナビゲーション
Jekyllサイトに多くのページがある場合、ページのナビゲーションを作成することを検討する必要があります。ナビゲーションリンクをハードコーディングする代わりに、プログラムでページのリストを取得して、サイトのナビゲーションを構築できます。
他のJekyllドキュメントにはデータファイルとの連携に関する情報が既にありますが、このチュートリアルでは、サイトのより堅牢なナビゲーションの構築について詳しく説明します。
Jekyllサイトでページを取得する主な方法は2つあります。
- YAMLデータソースにリストされているページを取得する。ページデータを`_data`フォルダ内のYAML(またはJSON、CSV)ファイルに保存し、YAMLプロパティをループ処理して、その値をテーマに挿入します。
- ページのフロントマターをループ処理してページを取得する。ページのフロントマターを調べて特定のプロパティを特定し、それらのページを返し、ページのフロントマターの値をテーマに挿入します。
以降の例では、基本的なナビゲーションシナリオから開始し、ページを返すさまざまな方法を示すために、より高度な要素を追加します。どのシナリオでも、3つの要素が表示されます。
- YAML
- Liquid
- 結果
`_data`ディレクトリのYAMLファイルは`samplelist.yml`と呼ばれています。
シナリオは以下のとおりです。
- シナリオ1:基本リスト
- シナリオ2:ソートされたリスト
- シナリオ3:2レベルのナビゲーションリスト
- シナリオ4:3レベルのナビゲーションリスト
- シナリオ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コンテンツには、ここで関連する2つの主要な形式の種類があります。
- マッピング
- リスト
`docs_list_title: ACME Documentation`はマッピングです。`site.data.samplelist.docs_list_title`を使用して値にアクセスします。
`docs:`はリストです。リストは各項目をハイフンで開始します。マッピングとは異なり、通常はマッピングのようにリストのプロパティに直接アクセスしません。リスト内の特定の項目にアクセスする場合は、通常の配列表記に従って、必要なリスト内の位置を特定する必要があります。たとえば、`site.data.samplelist.docs[0]`はリストの最初の項目にアクセスします。ただし、これはめったに行われません。
リストでは、通常`for`ループを使用してリスト内の項目を循環処理し、各項目に対して何かを行います。ナビゲーションメニューでは、通常、使用しているHTMLテーマのナビゲーション構造に基づいて、各リスト項目を`li`タグに挿入します。
各ハイフン(`-`)は、リスト内の別の項目を示します。この例では、各リスト項目に`title`と`url`の2つのプロパティしかありません。項目ごとに必要な数のプロパティを含めることができます。リスト内の各位置のプロパティの順序は関係ありません。
シナリオ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>
結果
項目はアルファベット順に表示されるようになりました。Liquidフィルターの`sort`プロパティは、リスト内の実際のプロパティである`title`に適用されます。`title`がプロパティでない場合は、別のプロパティでソートする必要があります。
詳細なフィルターオプションについては、Liquid配列フィルターを参照してください。この構文を単純に使用することはできません。
{% for item in site.data.samplelist.docs | sort: "title" %}{% endfor %}
`site.data.samplelist.docs`を`assign`タグまたは`capture`タグを使用して最初に変数に変換する必要があります。
シナリオ3:2レベルのナビゲーションリスト
見出しのタイトルとサブ項目の複数のセクションを含む、より堅牢なリストが必要なとします。これを行うには、この情報を格納するために、各リスト項目にさらにレベルを追加します。
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コードは`site.data.samplelist.toc`の`item`で最初のレベルを調べ、次に`item.subfolderitems`の`entry`で2番目のレベルのプロパティを調べます。`item`がループ処理する項目の任意の名前であるのと同じように、`entry`も任意の名前です。
シナリオ4:3レベルのナビゲーションリスト
前のセクションに基づいて、リストにさらに1レベルの深さ(`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`ステートメントを揃える
コードを明確にするために、`for`ループや`if`ステートメントなど、Liquidタグの先頭と末尾を揃えます。これにより、開始タグがいつ閉じられたかがわかります。コードがMarkdownページに表示される場合は、Markdownフィルターがコンテンツをコードサンプルとして扱わないように、開始HTMLタグと終了HTMLタグを左端に揃えます。必要に応じて、コード全体を`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 %}
結果
ここでは、`デプロイ`が現在のページであると仮定します。
`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>
結果
`デプロイ`ページは、その`バージョン`が`2`であるため除外されます。
シナリオ8:フロントマターのプロパティに基づいて項目を取得する
`_data`フォルダにYAMLファイルにナビゲーション項目を保存したくない場合は、`for`ループを使用して各ページまたはコレクションのフロントマターを調べ、フロントマターのプロパティに基づいてコンテンツを取得できます。
このシナリオでは、_docs
というコレクションがあると仮定します。コレクションはページよりも多くの場合優れており、ループ処理する項目のリストを絞り込むことができます。(大量の項目をループ処理するシナリオは避けてください。ビルド時間が長くなります。コレクションを使用すると、範囲を絞り込むことができます。)
このシナリオでは、docs
コレクションには、Sample 1、Sample 2、Topic 1、Topic 2、Widget 1、Widget 2の6つのドキュメントがあります。
コレクション内の各ドキュメントには、フロントマターに少なくとも3つのプロパティが含まれています。
title
category
order
各ページのフロントマターは以下のとおりです(簡潔にするためにまとめています)。
---
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
の中身を直接参照することはできません。
特定のカテゴリのコレクション内のすべてのドキュメントを取得したい場合、if
条件付きのfor
ループを使用して、特定のカテゴリを確認することができます。
<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>
結果は次のようになります。
これは、ナレッジベースを構築していて、各カテゴリに数十個のトピックがあり、各カテゴリが独自のページに表示される場合に役立つ可能性があります。
しかし、カテゴリ別に項目をソートし、カテゴリ名の下にグループ化したい場合(カテゴリ名をハードコーディングせずに)は、2つのフィルタを使用できます。
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
まず、ナビゲーションツリーのレンダリングに使用できるインクルードを作成します。このファイルは_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 %}
このインクルードは最初にこれを使用し、次に各項目のsubnav
プロパティを調べて、ネストされたリストを再帰的にレンダリングします。
結果