つぶやき Web拍手を送る

実践編その③
展示ページの目次を作ろう

前項で作品を展示するための個別ページができました。次は、これら個別ページの目次をindex.htmlに作っていきましょう。そのために11ty独自の機能、タグ(tags)コレクション(collections)を利用します。

タグとコレクション

もう1ページ作るぞの項やレイアウト(Layouts)ファイルを使ってみようの項で、11tyが用意している特別な属性があるということに触れました。ここで扱うタグtagsもそのような特別な属性の一つです。tags に何らかの値を設定すると、同じ値が設定されたページをまとめて一つのグループ (コレクション、collection) とみなしてくれるようになります。このようにコレクションにまとめることで、コレクション内に含まれるページのデータ(タイトル、URLなど)を列挙したり、コレクション内のページを決まったルールで並べて前後のページにリンクを貼ったりなどといったことが可能になります。

タグtags を使うことでページをグループ分けできます。

タグを設定しよう

それでは実際にやってみましょう。まずは上のタグとコレクションの機能を使って、展示ページをひとつのコレクションにまとめたいと思います。全てのページに同じタグtags を設定してやればいいので、「共通の設定をひとつのファイルにまとめておこう」の項で作成した設定用ファイルgallery.json が使えそうですね。ということでgallery.json に以下のように追記します。この時、"layout" の行の末尾にコンマ, がないと文法エラーで動かなくなってしまうので、気を付けてください。

gallery/gallery.json
{
    "layout": "gallery.html",
    "tags": "gallery"
}

タグに設定する値は自分で分かるものであればなんでもいいのですが、半角英数字のみで付けるのが無難だと思います。ここではgallery と設定しました。これで、gallery フォルダ内にある展示ページ全てをgallery という名前のコレクションにまとめることが出来ました。

とりあえずタイトルを列挙してみよう

展示ページをまとめたコレクションが出来たので、次はこれを使ってindex.html に目次を作っていきます。最終的にはサイト完成図のように各ページへのリンク(カード型)にしたいのですが、いきなりこれを作るのは大変です。簡単なところから少しずつ進めましょう。

まずは展示ページのタイトルだけを列挙したリストを作ってみます。とりあえず詳しい説明は後にして、一度書いてみましょう。index.html の展示ページ目次予定地に、以下のように記述します。

index.html
 <!-- 前略 -->
 <section>
   <h2>Gallery 1/個別ページの例</h2>
   <p>展示ページの目次がここに来る予定</p>
+  <ul>
+    {% for item in collections.gallery %}
+	    <li>{{ item.data.title }}</li>
+    {% endfor %}
+  </ul>
 </section>
 <!-- 後略 -->

この状態でindex.htmlをブラウザで見てみましょう。以下のようにgalleryフォルダ内に作ったページのタイトルがリストでずらりと並べば成功です!ここではとりあえず表示順は気にしないでおきましょう。 galleryフォルダ内のページのタイトルのリストが出来た

それでは、今index.htmlに追記した以下のコードについて、詳しく見ていきます。

<ul>
  {% for item in collections.gallery %}
	  <li>{{ item.data.title }}</li>
  {% endfor %}
</ul>

まずは2行目のcollections.galleryという記述から……これは11tyでコレクションのデータを呼び出すための書き方です。collectionsとドット. の後にコレクションの名前を続けて書くと、そのコレクションに属するページのデータの配列――複数のデータが順番に並んで入った箱のようなもの――を呼び出すことができます。

次に2行目全体の{% for item in collections.gallery %} ……これはLiquidの文法で、 反復処理(ループ処理) を行う書き方です。基本的な書き方は以下の通りです。

{% for xxx in yyy %} 
 この間に繰り返したいコードを書く
{% endfor %}

このように書くことで、配列yyyの持っている要素と同じ回数だけ、{% for xxx in yyy %}{% endfor %} の間の記述(「この間に繰り返したいコードを書く」の部分)を繰り返し出力してくれます。
また、xxx の部分はループ内で使える変数の名前となり、

……が変数xxxに代入され、ループの中で呼び出して使うことができるようになります。今回の場合だと

{% for item in collections.gallery %}

と書いたので、collections.gallery内のデータを1つずつ取り出して変数itemとして扱うことができます。

残りは3行目の<li>{{ item.data.title }}</li> ……外側の<li></li> は普通のhtmlなのでいいとして、内側の{{ item.data.title }} を見てみます。この中のitem には、先ほど述べた通りcollections.gallery の要素の中の一つ……つまり、あるページのデータが代入されています。
そして、11tyではページのデータ.data.属性名 という書き方をすることで、ページのフロントマターなどで設定した属性を呼び出すことができます。つまり、{{ item.data.title }} はページに設定された属性title を呼び出す記述だったという訳です。

ここまでの説明を踏まえて、

<ul>
  {% for item in collections.gallery %}
  <li>{{ item.data.title }}</li>
  {% endfor %}
</ul>

このコードがどういう動きをしているのか図解してみます。collections.galleryはコレクションgalleryに属しているページのデータの配列(データが並んで入っている箱のようなもの)で、その配列内の要素の数だけ{% for xxx in yyy %}{% endfor %}の中身をループします。
まず、最初のループでは1番目のページのデータが変数itemに入って……

1回目のループの様子

2回目のループでは2番目のページのデータが……

2回目のループ

こんな感じでx回目のループでは……

x回目のループ

と、このようなことがデータの数だけ繰り返されます。 その結果……

ループの結果

<ul></ul> の間に<li>ページのタイトル</li> といった記述がコレクションgallery に属するページの数だけ繰り返し出力され、タイトルを列挙したリストになった……という訳です。なんとなく動きを分かってもらえたでしょうか?

他のデータも呼び出してみよう

それでは、上記の手順を応用して目次に必要なデータを呼び出していきましょう。

まずはサムネイル画像……今回はあらかじめサムネイル用に縮小した画像をsrc/thumbnailsフォルダに用意し、これを呼び出して使うことにします。 用意したサムネイル画像(エクスプローラー表示)

タイトルの時と同様に、各ページのフロントマターにthumbnailという属性を用意してサムネイル画像のパスを設定してやります。

例) gallery/001.html
 ---
 title: 展示ページ①
+thumbnail: /src/thumbnails/001.jpg
 ---

そしてindex.htmlの、先ほどタイトルを表示させた<li></li>タグ内に画像を呼び出すコードを追記します。<img>タグのsrc属性で、直接画像のパスを指定する代わりに、今ページに設定した属性thumbnailを呼び出しましょう。

index.html
 {% for item in collections.gallery %}
 <li>
   {{ item.data.title }}
+  <img src="{{ item.data.thumbnail }}" alt="">
 </li>
 {% endfor %}

ちゃんと画像は表示されましたでしょうか?

サムネイル画像を呼び出した

次に、目次から各ページへのリンクを張りたいので、サイト内における各ページのパスを呼び出したいですね。
ここで、各ページのテンプレートのパスや生成されたページのサイト内でのパスなど、一部のデータは11tyが自動で属性に設定しておいてくれます。どのようなデータがあり、どんな属性名で呼び出せるかは公式ドキュメントに記載がありますが、今回呼び出したいページのパスもこのようなデータの一つです。このようなデータを呼び出したいときは……

index.html
 {% for item in collections.gallery %}
 <li>
     {{ item.data.title }}
     <img src="{{ item.data.thumbnail }}" alt=""><br>
+    {{ item.page.url }}
 </li>
 {% endfor %}

先ほどのデータを呼び出していた時に.data となっていた部分を.page に書き換え、そのあとに呼び出したい属性名を書けばOKです。今回は{{ item.page.url }} で、ページのパスを呼び出すことができます。今は分かりやすいように<li> タグ内に直接記述してページ上に表示されるようにしましたが、実際にリンクを張るときは<a> タグのhref 属性にこの値を置いてやればいいですね。

日付データの扱い方

実は、11tyが自動で設定してくれる属性の中には日付のデータもあります。特に設定しなければテンプレートファイルを作成した日時が設定されますが、自分で上書きすることも可能です。コンテンツの作成・更新日時を表示させたいときに使えそうです。今回のサンプルページでも目次のところに日付を表示させたいので、とりあえず呼び出して表示させてみましょう。日付データを呼び出すには{{ item.page.date }}です。

index.html
 {% for item in collections.gallery %}
 <li>
   {{ item.data.title }}
   <img src="{{ item.data.thumbnail }}" alt=""><br>
   {{ item.page.url }}<br>
+  {{ item.page.date }}
 </li>
 {% endfor %}

これで表示を確認してみると…… 曜日や時刻も表示されている…? ?!

日付だけでなく時間やタイムゾーンまで表示されてしまいました。

内部的にはこのように日付・時間のデータが保持されているのですが、全部表示させたいということはあまりないですよね。このとき、Liquidのフィルターという機能を使うことで、日付データを好きな形に成形することができます。フィルターとは、変数などの何らかの値の後にパイプ(縦棒)| を付け、そのあとにやりたい処理を記述することで、値に対してその処理をして出力してくれるという機能です。

……と説明するより、実際のコードを見た方が早いでしょう。今回は日付を2024/07/22のような形式で表示させることにします。これを実現するには……

{{ item.page.date | date: "%Y/%m/%d" }}

このように書きます。とりあえず先ほどの{{ item.page.date }} の部分を上のように書き換えてみて、日付の表示が年/月/日の形式で表示されることを確かめてみましょう。

このコードについて少し説明すると、日付のデータ(item.page.date)の後ろに続けて| date: "表示させたいフォーマット" と書くことで、日付データを好きな形式にフォーマットして表示させることができる、というものになっています。
この表示させたいフォーマット 部分では、%Yが西暦(4桁)、%mが月、%dが日を表していて、上のコードではそれを/で繋いだので年/月/日の形式で表示されたという訳です。日付のデータを表すための%Y%mなどの文字列以外はそのまま表示されるので、表示させたいフォーマット 部分をうまいこと書き換えてやれば2024-07-22や2024.07.22、2024年07月22日といったような表示をさせることももちろん可能です。また、上記の%Y%m%d 以外にも日付のデータ(年月日、曜日、時間、分など)を表すための記法があるので、詳しく知りたい場合はこちら(Liquid公式のドキュメント)やこちら(strftime reference and sandbox)を参考にしてみてください。

ここまでは11tyで自動設定された日付を表示させてきましたが、前述の通り自分で任意の日付を設定することも可能です。ページのフロントマターにdate: YYYY-MM-DD形式で記述することで、任意の日付を設定できます。

例) gallery/001.html
 ---
 title: 展示ページ①
 thumbnail: /src/thumbnails/001.jpg
+date: 2024-01-23
 ---

今回は、展示ページ①~⑥が古い~新しいの順になるように日付を設定しておきます。

ここで、目次に表示させてきたデータの並び順を見てみましょう。日付に注目すると、上から下に古い~新しいとなっているかと思います。

日付順に並んでいる
上が古い、下が新しい となっている

ここで、ページの日付を変えてみる……例えば上の図の展示ページ②の日付を展示ページ③より後の、3/29などにしてみると……

日付順に並び替わった

日付の順番に沿ってページの並びが変化しました。

このように、11tyのコレクション内のデータの順番は、各ページの日付を基準として昇順に並ぶのがデフォルトとなっています。もしもこれを逆順で表示させたいときは……

index.html
+{% for item in collections.gallery reversed %}
 <li>
     {{ item.data.title }}
     <img src="{{ item.data.thumbnail }}" alt=""><br>
     {{ item.page.url }}<br>
     {{ item.page.date | date: "%Y/%m/%d" }}
 </li>
 {% endfor %}

このように最初の{% for … %}の最後にreversedと追記することで元の並びと逆……新しい~古いの順番で表示させることができます。個人的に新しいものが上にある方が好きなので、今回はこの状態で進めることにします。 日付の新しいものが上に、古いものが下になった

目次の仕上げ~見た目を整えよう~

さて、ここまでの手順で目次を作るために必要なデータは揃ったので、形を整えて仕上げていきましょう。今回のサンプルでは<ul>タグにgallery_1というクラス名を指定したうえで<li>タグ内を以下のようなhtml構成にすることで、cssが適用されていい感じの見た目になるようにしています。

<li>
  <a href="[ページのパス]">
    <img src="[サムネイル画像のパス]" alt="">
    <span class="thumbnail_caption">
      [ページタイトル]<br>
      <small>[日付]</small>
    </span>
  </a>
</li>

この中の[] で囲まれた部分が各ページで異なる部分……これまでの解説で呼び出してきたデータに対応しているのでそれぞれ当てはめていくと……

<li>
  <a href="{{ item.page.url }}">
    <img src="{{item.data.thumbnail}}" alt="">
    <span class="thumbnail_caption">
      {{ item.data.title }}<br>
      <small>{{ item.page.date | date: "%Y/%m/%d" }}</small>
    </span>
  </a>
</li>

このようなコードになりました。Gallery 1部分のセクション全体のコードは下記の通りです。

index.html
<section>
    <h2>Gallery 1/個別ページの例</h2>
    <p>展示ページの目次がここに来る予定</p>
    <ul class="gallery_1">
        {% for item in collections.gallery reversed %}
        <li>
            <a href="{{ item.page.url }}">
                <img src="{{item.data.thumbnail}}" alt="">
                <span class="thumbnail_caption">
                    {{ item.data.title }}<br>
                    <small>{{ item.page.date | date: "%Y/%m/%d" }}</small>
                </span>
            </a>
        </li>
        {% endfor %}
    </ul>
</section>

実際にこのコードに書き換えて、生成されたページをブラウザで見てみましょう。 個別ページへの目次が完成!

上の画像のように表示されれば成功です!ちゃんとサムネイルをクリックすることで該当のページに移動できることも確認しておきましょう。


今回は、11tyの機能の解説のために各ページに設定されたデータを列挙することから始め、最後にhtmlを整えるという順番でやってきました。しかし実際にご自身でサイトを構築する時は、一度11tyのことは忘れて普通にhtml/cssを書いてデザインを決めてから、ページの属性などのデータを呼び出す必要があるところを置き換えたり設定したりしていく方がやりやすいでしょう。