ナビゲーション
Jekyllサイトに多くのページがあるなら、ページのナビゲーションを作りたくなるでしょう。ナビゲーションリンクをハードコーディングする代わりに、ページのリストをプログラムで取得してサイトのナビゲーションを構築することができます。
Jekyllドキュメンテ—ションにはdataファイルとのやりとりについての情報がありますが、ここではより堅牢なナビゲーションの構築に挑戦します。
jekyllサイトのページを取得するには主に2つの方法があります。
- YAMLデータソースにリストされているページの取得。
_data
フォルダのYAML(やJSONやCSV)ファイルにページデータを保管し、YAMLプロパティをル—プさせ、その値をテーマに挿入します。 - ページのfront matterをループしてページを取得。front matterを通じページを識別し、ページを返し、そのページのfront matterの値をテーマに挿入します。
以下では、基本的なナビゲーションのシナリオから始め、ページを返す異なる方法のデモンストレーションに洗練された要素の追加を例示します。どのシナリオでも、3つの要素があります。
- YAML
- Liquid
- Result
_data
ディレクトリのYAMLファイルは、samplelist.yml
という名前です。
シナリオは次の通りです。
- シナリオ1:基本的なリスト
- シナリオ2:リストのソート
- シナリオ3:2レベルのナビゲーションリスト
- シナリオ4:3レベルのナビゲーションリスト
- シナリオ5:YAMLリストの選択にページ変数を使用する
- シナリオ6:現在のページにactiveクラスを適用する
- シナリオ7:条件付きでアイテムを含める
- シナリオ8:front matterのプロパティに基づくアイテムの取得
- シナリオ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>
Result
ACME Documentation
この架空のサンプルの結果では、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>
Result
アルファベット順になりました。Liquidフィルタのsort
プロパティがリストの実際のプロパティであるtitle
に適用されました。title
がプロパティで無ければ、他のプロパティでソートする必要があります。
フィルタのオプションはLiquid array filterを見てください。この構文を以下のようにシンプルにすることはできません。
{% 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 %}
Result
この例では、Group 1
が最初のリストアイテムです。このリストアイテムのサブページに、そのリスト(subfolderitems
)をプロパティとして含んでいます。
Liquidコードはfor item in site.data.samplelist.toc
でファーストレベルを、for entry in item.subfolderitems
でセカンドレベルを見通しています。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>
Result
Group 1
Group 2
Group 3
この例では、if site.data.samplelist.toc2[0]
でYAMLレベルに本当にアイテムがあるのかを確認しています。ポジション[0]
に何もなければ、そのレベルの処理をスキップします。
ProTip: for
ループとif
文を揃える
コードをきれいに保つために、for
ループとif
文のように、Liquidタグの始まりと終わりを揃えます。この方法で、タグがどこで始まりどこで終わったかが分かりやすくなります。コードがMarkdownページに表示される場合は、Markdownフィルタがコードサンプルとして扱わないように、HTMLタグの開始と終了を左端にあわせてください。 必要に応じて、コードサンプル全体をdiv
タグで囲むことで、コードにコードを補足するHTMLタグを確実に含めることができます。
シナリオ5:YAMLリストの選択にページ変数を使用する
いくつかのドキュメンテーションのセットでサイドバーを変更したい場合を考えます。サイトに3つの異なるプロダクトがあり、それぞれにあわせて3つの異なるサイドバーが必要だとします。
ページのfront matterにサイドバーのリストの名前を格納し、その値を動的にリストに渡します。
Page front matter
---
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>
Result
このシナリオでは、ページのfront matterからその値をfor
ループに渡しました。割り当てられた変数が文字列ではなくデータ参照である場合は、front matterの値を参照するために(中括弧ではなく)大括弧を使用する必要があります。
更なる情報は、LiquidのドキュメンテーションのExpressions and Variablesを見てください。ドット表記が使えない場所で大括弧が使用されます。この詳細はStack Overflow answerで読むことができます。
シナリオ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 %}
Result
この例ではDeployment
が現在のページとしています。
(YAMLファイルに格納されている)item.url
がpage.url
と確実に一致するようにするには、{%raw%} /tutorials/navigation/ {%endraw%}をページに出力すると便利です。
シナリオ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>
Result
Deployment
はversion
が2
ですので書き出されていません。
シナリオ8:front matterのプロパティに基づくアイテムの取得
ナビゲーションアイテムを_data
フォルダのYAMLファイルに格納したくない場合、各ページやコレクションのfront matterを取得してfor
ループを使用し、front matterのプロパティを元にコンテンツを取得します。
このシナリオでは、_docs
というコレクションがあるとします。コレクションは、ループできるリストを持っているため、ページよりよい場合が多いです。(ビルド時間が長くなるため、多数の項目をループ処理するシナリオは避けてください。 コレクションは範囲を狭めるのに役立ちます。)
このシナリオでは、docs
コレクションにSample 1, Sample 2, Topic 1, Topic 2, Widget 1, そしてWidget 2の6つのドキュメントがあるとします。
コレクションの各ドキュメントにはfront matterに最低3つのプロパティを含めます。
title
category
order
各ページの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
---
category
をドキュメントのfront matterで使用していても、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>
結果は以下のようになります。
これは、ナレッジベースを設定し、各カテゴリに多数のトピックがあり、各カテゴリをそれぞれのページに表示する場合に便利です。
カテゴリ名をハードコーディングせずに、カテゴリ別にアイテムをソートし、それらをカテゴリ名の下にグループ化する場合を考えます。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 %}
Result
コードを見ていきましょう。まず、コレクションの内容(site.docs
)の為の変数(mydocs
)を設定しています。
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
をプリントしています。
カテゴリ名を取得した後、sort
フィルタでorder
プロパティ順に並べたドキュメントを変数items
に設定します。コンテンツのitems
配列にアクセスするためにcat.items
を使用しています。sort
フィルタでアイテムの番号で昇順に並べ替えています。
for item in items
ループは各item
を確認し、リストのアイテムをリンクするためにtitle
とurl
を取得しています。
group_by
フィルタの詳細はJekyll’s Templates documentationやthis Siteleaf tutorialをご覧ください。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></li>
{% if item.subnav %}
{% include nav.html nav=item.subnav %}
{% endif %}
{% endfor %}
</ul>
レイアウトやページで使用するには、単にテンプレートをincludeし、nav
パラメータを渡します。以下では、YAML front matterを参照するためにpage.nav
を使用しています。
{% include nav.html nav=page.nav %}
インクルードではこれをまず使用し、入れ子のリストを再帰的にレンダリングするためにsubnav
の各項目を確認します。
Result