つぶやき Web拍手を送る

実践編その④
展示ページに前後のページへのリンクを作ろう

今度は、実践編その② 作品(イラスト・写真)を展示するページを作ろうで作成した各展示ページに、前後のページへのナビゲーションリンクを付けてみたいと思います。ちょうど共通の設定をひとつのファイルにまとめておこうの項で作った展示ページ専用のレイアウトgallery.htmlがあるので、これをいじってナビゲーションを追加していきましょう。

_includes/gallery.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="/src/style.css">
    <title>{{ title }} - Sample Site</title>
</head>

<body>
    {% include "partials/header.html" %}
    {{ content }}
    <p>このページはgallery.htmlを使っているよ</p>
    {% include "partials/footer.html" %}
</body>

</html>

上記のコードの、コンテンツ{{ content }} とフッター{% include "partials/footer.html" %} の間、今<p>タグがある部分にナビゲーションを置くことにします。今回は先にナビゲーションを表示させるためのhtmlのひな型を示しておきましょう。

<section>
  <nav class="navInCollection">
    <ul>
        <li class="navPrev">
          <a href="[前のページのパス]">[前のページのタイトル]</a>
        </li>
        <li class="navNext">
          <a href="[次のページのパス]">[次のページのタイトル]</a>
        </li>
    </ul>
  </nav>
</section>

このhtmlをgallery.html{{ content }}{% include "partials/footer.html" %}の間に追記します。この状態でどれでもいいので展示ページを開いてみてください。 ナビゲーション(見た目だけ)が追加された展示ページ このようにナビゲーションっぽいものが表示されたと思います。もちろんこの状態ではリンクが繋がっておらず、タイトルも固定の文字列が表示されるだけなので、ここからナビゲーションとして機能するように作り変えていきましょう。

フィルター getPreviousCollectionItem / getNextCollectionItem

さて、前後のページへのリンクを作るには当然 「今いるページの前/次のページの情報」 が欲しいわけですが、ちょうどそのような機能が11tyには備わっています。
それが、getPreviousCollectionItem /getNextCollectionItem というフィルターです。実際にテンプレート(ページ内)で使用する時の記述例は以下の通りです。

collections.posts | getPreviousCollectionItem: page
collections.posts | getNextCollectionItem: page

|の左側には、 「順番の基準となるコレクション」 を記述します。この書き方はとりあえずタイトルを列挙してみようの項で出てきたコレクションの呼び出し方、collections.コレクション名と同じですね。
そして、|に続けてgetPreviousCollectionItem: page もしくはgetNextCollectionItem: page と書くことで、「順番の基準となるコレクション」内における、今いる「このページ」の前/次に当たるページの情報を得ることができます。(Previousの方が前、Nextの方が次)

ちなみに、get***CollectionItem の後の: page の記述は省略することも可能です。つまり、下のように書いても同じように動きます。

collections.posts | getPreviousCollectionItem
collections.posts | getNextCollectionItem

これで前後のページの情報を得る方法が分かったので、実際にこれを用いてナビゲーションを作っていきましょう。まずはページの情報を扱いやすくするために、変数に設定しておきましょう。以下のコードをgallery.htmlの、ナビゲーション部分のタグより前に追加します。

{% assign previousPost = collections.gallery | getPreviousCollectionItem: page %}
{% assign nextPost = collections.gallery | getNextCollectionItem: page %}

ここで、{% assign XXX = YYY %} という書き方はLiquidの文法で、XXXという名前の変数にYYYというデータをセットする……といった感じに捉えてもらえばよいです。つまり、上のコードではpreviousPostという名前の変数に前のページのデータを、nextPostという名前の変数に次のページのデータをセットしたことになります。

次に、このデータを利用してナビゲーションに情報を入れていきます。今、ナビゲーション部分のコードは以下の通りです。

<section>
  <nav class="navInCollection">
    <ul>
        <li class="navPrev">
          <a href="[前のページのパス]">[前のページのタイトル]</a>
        </li>
        <li class="navNext">
          <a href="[次のページのパス]">[次のページのタイトル]</a>
        </li>
    </ul>
  </nav>
</section>

このパスやタイトルの部分を、前後のページのデータから引っ張ってきて置き換えればいいわけです。ページに設定されているデータはとりあえずタイトルを列挙してみようで解説した通り、ページのデータ.data.属性名 で呼び出すことができます。今回は、このページのデータ部分に前後のページのデータがセットされた変数previousPost /nextPost を当てはめてやればよさそうです。とりあえずタイトルの部分だけ置き換えてみましょう。

_includes/gallery.html
 <section>
   <nav class="navInCollection">
     <ul>
         <li class="navPrev">
+           <a href="[前のページのパス]">{{ previousPost.data.title }}</a>
         </li>
         <li class="navNext">
+          <a href="[次のページのパス]">{{ nextPost.data.title }}</a>
         </li>
     </ul>
   </nav>
 </section>

このようにタイトルが表示されるはずです。 ナビゲーションにページのタイトルが表示された!

タイトルがうまく置き換えられたら、<a>タグのパスも置き換えましょう。ページのパスを呼び出すときは他のデータも呼び出してみようで解説した通り、ページのデータ.page.urlと書きます。

_includes/gallery.html
 <section>
   <nav class="navInCollection">
     <ul>
         <li class="navPrev">
+        <a href="{{ previousPost.page.url }}">{{ previousPost.data.title }}</a>
         </li>
         <li class="navNext">
+        <a href="{{ nextPost.page.url }}">{{ nextPost.data.title }}</a>
         </li>
     </ul>
   </nav>
 </section>

これでリンクを繋げられました!ちゃんと正しいページに繋がっているか、ナビゲーションをクリックして確認しておきましょう。


これで前後ページへのナビゲーションは完成!……といきたいのですが、まだ気になる点が2つほどあります。

まず1つ目、一番最初(もしくは最後)に当たるページのナビゲーションを見てみると…… 左側のナビが矢印だけになっている

一番最初(最後)なので、当然その前(次)にあたるページなんてある訳がありません。その結果、虚しく矢印だけが表示された状態に……特にエラーになったりはしないので気にならなければこのまま置いておいてもいいのですが、今回は前(次)にページがなければその方向のナビは表示しないようにしてみましょう。

条件によって表示する・しないを分けるには、Liquidのif文を使用します。今回やりたいことを実現するコードは以下の通りです。

{% if previousPost %}
	<li class="navPrev">
	  <a href="{{ previousPost.page.url }}">{{ previousPost.data.title }}</a>
	</li>
{% endif %} 
{% if nextPost %}
	<li class="navNext">
	  <a href="{{ nextPost.page.url }}">{{ nextPost.data.title }}</a>
	</li>
{% endif %}

具体的に上記のコードの理屈について説明していきます。まず、Liquidのif文の基本的な書き方は次の通りです。

{% if condition %}
<!-- 条件が真のときに出力したいコード -->
{% endif %}

conditionの部分には条件を表す式を書きます。ここの条件がtrue (真、正しい)ならば{% if %}{% endif %} に挟まれたコードが出力され、条件がfalse(偽、正しくない)なら何も出力されません。

次に実際のコードを見てみると、condition ――条件式を書くところにページのデータをセットする変数previousPost/nextPost が入っています。条件「式」とは言っていますが、式ではなく値(変数)を単独で入れるだけでも、その値によってtruefalseかが判定されます。ここがポイントで、11tyでは値が 「空っぽ」 ならばfalse、逆に空っぽでなければtrueと判定されるようになっています。

上記の説明を踏まえてもう一度コードを見てみます。nextもpreviousも理屈は一緒なのでpreviousの方に注目すると……

{% if previousPost %}
	<li class="navPrev">
	  <a href="{{ previousPost.page.url }}">{{ previousPost.data.title }}</a>
	</li>
{% endif %}

「前のページが存在する」場合、変数previousPostには「ページのデータが入っている」状態になっています。つまりifの条件式に書かれているpreviousPost「空っぽではない」 ……すなわちtrueと判定されます。したがって{% if %}{% endif %}の間のコードが出力される=ナビゲーションが表示されます。

一方で「前のページが存在しない」場合、変数previousPostには何のデータも入らない、 「空っぽ」 の状態になります。ということは条件式に書かれたpreviousPostfalseとみなされ、{% if %}{% endif %} の間のコードは出力されない=表示されないという訳です。 左側のナビが非表示になった

これで最初/最後のページには前/次に行くナビゲーションが表示されなくなりました。


そして2つ目の気になる点……目次では前⑥⑤④③②①次の順番にページを並べたのですが、ナビゲーションでは前①②③④⑤⑥次という並びになっています。これは日付データの扱い方の項目で、もともと①~⑥の順番だったのを目次では逆に表示させるようにしただけなので当然といえば当然です。

この逆順の並びに合わせてナビゲーションを作りたい場合、2つの解決策が考えられます。

解決策①コレクションの並びを逆順にする

とりあえず下記のように書き換えてみましょう。

_includes/gallery.html
 <!-- 前略 -->
+{% assign previousPost = collections.gallery | reverse | getPreviousCollectionItem: page %}
+{% assign nextPost = collections.gallery | reverse | getNextCollectionItem: page %}
 
 <section>
   <nav class="navInCollection">
     <ul>
       {% if previousPost %}
       <li class="navPrev">
         <a href="{{ previousPost.page.url }}">{{ previousPost.data.title }}</a>
       </li>
       {% endif %} 
       {% if nextPost %}
       <li class="navNext">
         <a href="{{ nextPost.page.url }}">{{ nextPost.data.title }}</a>
       </li>
       {% endif %}
     </ul>
   </nav>
 </section>
 <!-- 後略 -->

変わったのは変数previousPost/nextPost にページのデータをセットする部分の2行だけ…… | reverse の記述が増えました。 この「|の後に何か書く」のは「フィルター」という機能を使うための記述方法で、| の左にある値に対して右に書いた処理を行う、という意味でした。そして「|の後に何か書いた」後に続けて「|の後に何か書く」ことも可能です。つまり、

{{(数値とか文字列とか配列とか) | 処理A | 処理B | 処理C |… }}

上記のように書けば、値に対して処理Aを行ったものに対して処理Bを行い、それに対して処理Cを行い……となります。

そしてreverse というのはLiquidに元々備わっているフィルターの一つで、「配列などの並び順を逆にする」効果があります。

つまり、上のコードでは「順番の基準となる『gallery』コレクション」の「並びを逆にした」もの「……を基準に前/次に当たるページのデータを取得する」といった処理が行われていたので、目次と同様に⑥⑤④③②①の並びを基準に前/次を判定してナビゲーションが作られるようになる、という訳です。 目次の並びとナビの前後が同じになった

解決策②コード上でナビゲーションの順番を入れ替える

⑥⑤④③②①の並びの時とナビゲーションの順番が逆になってしまうのだから……

{% assign previousPost = collections.gallery | getPreviousCollectionItem: page %}
{% assign nextPost = collections.gallery | getNextCollectionItem: page %}

<section>
    <nav class="navInCollection">
        <ul>    
        <!-- 先(左側)にNextを置いて -->          
            {% if nextPost %}
            <li class="navPrev">
                <a href="{{ nextPost.page.url }}">{{ nextPost.data.title }}</a>
            </li>
            {% endif %}
        <!-- 後(右側)にPreviousを置く --> 
            {% if previousPost %}
            <li class="navNext">
                <a href="{{ previousPost.page.url }}">{{ previousPost.data.title }}</a>
            </li>
            {% endif %}
        </ul>
    </nav>
</section>

Previousの部分とNextの部分をごそっと入れ替えてしまえばよいのです。これで「表示上は」解決策①と同じ状態になりました。

ただ、cssでnavPrevのクラス名があれば左に、navNextのクラス名なら右にと設定してしまっていたので、コードがちぐはぐな感じです。複数人でサイトを作っていたり、仕事で作っていたりするなら問題かもしれませんが、個人が趣味で作っているサイトなら作っている本人さえ分かっていれば・気にしなければ、このような力業で解決してしまうのもアリです。こちとらただの素人だぞ!見た目良ければ大体ヨシ!

目次の並びとナビの前後が(表示上は)同じになった
(閲覧者には関係ないので)ヨシ!

最後は適当な感じになってしまいましたが、ひとまずここまでで「展示用の個別ページを作る」「個別ページの目次を作る」「個別ページに前後のページへのナビゲーションを作る」ことができました。

今度は、サムネイルをクリックするとモーダルで大きい画像が表示されるタイプのギャラリーを作ってみましょう。