おもこん

おもこんは「思いつくままにコンピュターの話し」の省略形です

Jekyllを勉強中(7)

追記 2022/8/23: コレクションの名前に「documents」を使っていましたが、この名前はJekyll自身が別の意味で使う変数になっていたので、「chapters」に変更しました。 また、_config.ymlの中で使っていたsort_byキーはJekyll4.0以降で可能ですが、GitHubのJekyllのバージョンは3.9.2でまだサポートされていません。 そこでsort_buキーを使わないように変更しました。

追記 2022/8/19: GitHubにあげる場合、baseurlの設定が必要な場合があります。 修正点は2つで(1)_config.ymlにbaseurlを設定する(2)レイアウトにrelative_urlフィルタを追加する、です。 baseurlの解説はJekyllを勉強中(9)にあります。 この記事中のプログラムは修正済みです。

今回は、レイアウトについて説明します。 あわせて、Jekyllが採用しているテンプレート言語のLiquidについても簡単な説明をします。

ファイルの構成と配置

このシリーズで例にとりあげてきた「Jekyll tutorial for beginners」の構成を考えてみます。

このうちドキュメントは「_chapters」フォルダに入れることにします。 ディレクトリ名の最初にアンダースコア(_)がつくのは、Jekyllのコレクションという機能を使うからです。

+--- _config.yml
+--- index.md
+--- about.md
+--- toc.md
+---_chapters
|       +--- doc1.md
|       +--- doc2.md
|       + ... ... ...
+---assets
|       +--- css
|       +--- images
|       +--- js
+---_site
... ... ...

トップディレクトリ以下のファイル構成が出力ディレクトリ_site以下に反映されます。 ただし、Markdown=>HTML、Scss=>Cssと変換して生成されます。 生成されたページ相互に行き来できるようリンクを付けなければなりません。

コンテンツ内上部にリンクを横並びに設置する方法を取ることにします。 このリンクのことをコンテンツ・ナビゲーション(短くして「コナビ」)と呼ぶことにします。 これは、このブログの中での呼び方で、一般に使われるものではありません。 複数のファイルで同じコナビが使われるので、コナビはレイアウトに記述するのが良いです。

レイアウトhome.html

「index.md」「about.md」「toc.md」の3つのファイルのコナビを記述するレイアウトを「home.html」とします。 新たに_layoutsフォルダを作り、その下にhome.htmlを置きます。

---
layout: default
---
<ul class="nav">
  <li>
    <a href={{ "/index.html" | relative_url }}>トップページ</a>
  </li>
  <li class="nav">
    <a href={{ "/about.html" | relative_url }}>Aboutページ</a>
  </li>
  <li class="nav">
    <a href={{ "/toc.html" | relative_url }}>目次</a>
  </li>
</ul>
{{ content }}

フロントマターにlayout: defaultがあるので、デフォルトのレイアウトのコンテンツにhome.htmlのレイアウトが設定されます。

それぞれのページへのリンクが順序なしリストになっています。 リンクにかかっているフィルターの説明は「Jekyllを勉強中(9)」にあります。

クラスnavスタイルシートは「style.scss」に記述し、assets/cssフォルダに置きます。

---
---

@import "{{ site.theme }}";

ul.nav {
  display: flex;
}
li.nav{
  list-style: none;
  margin-left: 20px;
}

(注意)このファイルの内容はcssですが、インポートするファイルがscssなので、拡張子はscssにします。

site.theme_config.ymlで設定したthemeキーの値で、「jekyll-theme-leap-day」です。 同名のScssファイルがテーマに含まれています。

「index.md」「about.md」「toc.md」のフロントマターのレイアウト設定をdefaultからhomeに変更します。 ここまでで、index.mdを表示するとコンテンツの最初に3つのリンクが横並びに表示されます。

コンテンツ・ナビゲーション

コレクション

Jekyllにはコレクションという機能があります。 コレクションはページ(またはそのページを生成するファイル)を整理し、管理できるようにします。

例えば「doc1.md」「doc2.md」・・・は「チュートリアルのドキュメント」という括りでまとめられます。 これらを「_chapters」ディレクトリの下に入れることにより、他のファイルと区別でき、ルートディレクトリもすっきりします。 そしてコレクションは_config.ymlの中で定義します。 コレクション名はディレクトリ名から先頭のアンダースコアを取ったものになります。

collections:
  chapters:
    output: true
  • コレクションは「collections」をキーとするハッシュで表す
  • この例では「chapters」がコレクション名で、更にその属性がハッシュで表される
  • デフォルトではコレクションは出力されないので「output: true」で出力されるようにする

この定義により、Liquidのタグなどでsite.documentsが「documentsコレクション」を表すようになります。

Base URL

_config.ymlにBase URLの設定をします。 Base URLの説明は「Jekyllを勉強中(9)」にあります。

baseurl: /jekyll-tutorial-for-beginners

ドキュメントのレイアウト

ドキュメントではコナビに「前の章」「次の章」を加えます。 また、コナビはコンテンツの最初と最後につけることにします。 2箇所に同じコナビをつけるので、コナビを独立したファイルにし、レイアウトの中でインクルードします。

インクルード

インクルード対象のファイルは「_include」ディレクトリに置きます。

コナビを表すファイルは下記のリストのようになり、_include/doc_nav.htmlに保存されます。

<ul class="nav">
  <li>
    <a href={{ "/index.html" | relative_url }}>トップページ</a>
  </li>
  <li class="nav">
    <a href={{ "/about.html" | relative_url }}>Aboutページ</a>
  </li>
  <li class="nav">
    <a href={{ "/toc.html" | relative_url }}>目次</a>
  </li>
  {% if page.chap > 1  %}
    <li class="nav">
      <a href="doc{{page.chap|minus: 1}}.html">前の章</a>
    </li>
  {% endif %}
  {% if page.chap < site.chapters.size %}
    <li class="nav">
      <a href="doc{{page.chap|plus: 1}}.html">次の章</a>
    </li>
  {% endif %}
</ul>

上から10行まではhome.htmlのレイアウトに書いた内容と同じです。 {%raw%}{%%}{%endraw%}で囲まれたものはLiquidのタグと呼ばれます。 ここにはプログラムのコントロール・フロー(if, else, unlessなどの条件分岐)などを書くことができます。

{% if page.chap > 1  %}
  <li class="nav">
    <a href="doc{{page.chap|minus: 1}}.html">{{chap}}前の章</a>
  </li>
{% endif %}

ifの条件は「そのページの章(chap)が1より大きい」すなわち「第2章以降」で、このときは「前の章」が存在します。 条件が成立する時にifとendifで囲まれた部分が出力されますが、その出力内容は「前の章へのリンク」です。

例えば第3章の前は第2章で、ファイル名は「doc2.html」(変換後なので拡張子は.mdではない)になります。 数字の2にあたる部分はLiquidのオブジェクト({%raw%}{{}}{%endraw%}で囲まれた部分)で表されています。

{{page.chap|minus: 1}}

縦棒|は前にも出てきた「フィルター」というもので、左の出力を右の入力につなげます。 このときフィルターの動作はパイプの右側に記述します。 「minus」は引き算をします。 例えば第3章では「page.chap」は3なので、「minus: 1」はそこから1を引き、2になります。

Liquidには算術演算子が無く、計算はフィルターを使って行います。

{% if page.chap < site.chapters.size %}
  <li class="nav">
    <a href="doc{{page.chap|plus: 1}}.html">次の章</a>
  </li>
{% endif %}

ほとんど同じですが、site.chapters.sizeのところが新たな内容です。 変数siteにコレクション名をつけ、更にsizeをつけています。

  • site.chaptersはchaptersコレクションに属するページの配列を返す
  • sizeは文字列の文字数や配列の要素数を返す。 本来パイプ(|)とともに使われるフィルターだが、 タグ(<%raw%}{%%}{%endraw%}で囲まれた部分)では、ドット(.)記法が使える。

章の数が配列のサイズより小さい、すなわち「最後の章ではない」ときに「次の章」をコナビに入れます。

ドキュメントのレイアウト

ドキュメントのレイアウトはdocument.htmlです。

---
layout: default
---
{% include doc_nav.html %}
<h1>第{{page.chap}}章 {{page.title | escape}}</h1>
{{ content }}
{% include doc_nav.html %}

前に作ったdoc_nav.htmlを2ヶ所でインクルードしています。 インクルードのおかげで短くさっぱりした記述になっています。 コナビとコンテンツの間に「第何章」というタイトルをh1タグで入れました。 escapeフィルタは「Jekyllを勉強中(9)」で説明されています。

ドキュメント

ドキュメント(doc1.mdなど)のフロントマターにはchapなどを入れます。 例えば、第1章のドキュメントのフロントマターは次のようになります。

---
layout: document
title: Jekyll、GitHub Pagesとは
description: JekyllとHitHub Pagesの基礎知識
chap: 1
---
  • レイアウトはdocument.htmlを使う
  • タイトルとディスクリプションを指定=>default.html(document.htmlの親レイアウト)で使われる
  • chapは章の番号を表す=>document.htmlで使われる

目次

目次の生成にはforループを使います。 以下はtoc.mdソースコードです。

---
layout: home
title: 目次
description: チュートリアルの目次
---
## 目次

{% assign chaps = site.chapters | sort: "chap" %}
{% for doc in chaps %}
- [{{doc.chap}}章 {{ doc.title | escape }}]({{ doc.url | relative_url }})
{% endfor %}

下3行が目次を生成する部分です。

  • site.chaptersはchaptersコレクションに含まれるページの配列。 この配列を章の番号順にソートするために、フィルターが使われている。 | sort: "chap"はフロントマターのchapキーの値で配列をソートしたものを値とする。 (このソートされた配列は元の配列とは別オブジェクト)。 変数chapsにソート済み配列が代入される
  • for文は変数docに次々と配列要素を入れてループする
  • {{doc.chap}}はそのページの章番号になる
  • {{doc.title}}はそのページのタイトル
  • {{doc.url}}はそのページのURLアドレスになる

このようにして、各章へのリンクがリストとして生成されます。

Leap dayの日本語対応

Leap dayのJavascriptjQuery)は、見出しが日本語のときに上手く動作しません。 修正は、次のようにします。

  • assets/jsディレクトリを作る
  • Leap dayのgemの場所を調べる($ bundle info jekyll-theme-leap-dayでパスが表示される)
  • Leap dayのassets/js/main.jsを最初に作ったassets/jsの下にコピーする
  • 下記のようにmain.jsを修正する
... ... ...
... ... ...
$(window).resize(sectionHeight);

var tag_number = 1;

$(function() {
  $("section h1, section h2, section h3").each(function(){
    $("nav ul").append("<li class='tag-" + this.nodeName.toLowerCase() + "'><a href='#tag_" + String(tag_number) + "'>" + $(this).text() + "</a></li>");
    $(this).attr("id","tag_"+String(tag_number));
    ++tag_number;
    $("nav ul li:first-child a").parent().addClass("active");
  });
});
... ... ...
... ... ...

変更箇所は4ヶ所です。

  • var tag_number = 1;
  • $(function()から2-3行目のString(tag_number)に置き換える部分(2ヶ所)
  • ++tag_number

手作業では間違いやすいのでコピペするのが良いと思います。

ここまでで、Jekyllの使い方がかなり分かってきたのではないでしょうか。 次回はJekyllでブログを書く方法を扱おうと思います。