ナビゲーション

24 January 2017 tomjoht tomjoht

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:基本的なリスト

ページの基本的なリストを返します。

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

この架空のサンプルの結果では、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タグに各リストを挿入します。

各ハイフン(-)は、リストの他のアイテムであることを示します。この例では、titleurlの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.docsassigncaptureタグで変数に変換する必要があります。

シナリオ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

この例では、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.urlpage.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

Deploymentversion2ですので書き出されていません。

シナリオ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.categorycategoryの内部を直接見ることはできません。

コレクション内の特定のカテゴリのドキュメントを簡単に取得したい場合、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>

結果は以下のようになります。

Getting Started

これは、ナレッジベースを設定し、各カテゴリに多数のトピックがあり、各カテゴリをそれぞれのページに表示する場合に便利です。

カテゴリ名をハードコーディングせずに、カテゴリ別にアイテムをソートし、それらをカテゴリ名の下にグループ化する場合を考えます。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

Getting-started

Configuration

Deployment

コードを見ていきましょう。まず、コレクションの内容(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を確認し、リストのアイテムをリンクするためにtitleurlを取得しています。

group_byフィルタの詳細はJekyll’s Templates documentationthis 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