こなさんみんばんわ。
さぁ成人の日も終わったことだし今日から本気出すわ(それ俺何の関係もないイベント)

そんな訳でどうやら世間では三連休とかだったらしいですが、僕は連休も何もあったもんじゃないので、ていうよりずっと連休みたいなもんなので😭 ここ数日ものんきにこのサイトの機能強化を行っております。

そのひとつとしまして、世間様の普通のブログにはだいたいある、前後を辿るリンク…古い or 新しい記事だったり、複数ページ間のナビゲーションですね…を付けてみました。今までもいちおうそれっぽいものはあったといえばあったんですが、アーカイブ系のページだけで単独記事にはなかったのですよ。

という訳で、本日はその作業メモです(ちょっとだけ他の話もあります)。ちなみにこのサイトは SSGJekyll を使ってるので、内容はほぼ Jekyll 限定の話です。

前提条件

最初に、このサイトで使っている Jekyll の大ざっぱな構成です。

各記事ページのひとつ前・ひとつ後のリンクを得る(簡単)

記事単独のページにひとつ前の(古い)記事とひとつ後の(新しい)記事へのリンクを付けるのはとても簡単。Jekyll のページ変数にはそのものズバリ page.next, page.previous というものがあり、その中には相当する記事のオブジェクトが入ってるので、そこから URL やタイトルを引っ張ってくるだけです。

<!-- 例えば前の記事の URL -->
{{ page.previous.url }}
<!-- 例えば次の記事タイトル -->
{{ page.next.title }}

相当する記事がない場合は単に nil が返ってくるだけなので、あとは条件分岐で必要な時だけ前後の HTML が出力されるようなコードを、レイアウトファイルなどに書けばいいです。

<ul>
  <!-- 前のページ -->
  {% if page.previous %}
  <li>
    <a href="{{ page.previous.url }}">
    前の記事: {{ page.previous.title }}
    </a>
  </li>
  {% endif %}
  <!-- 次のページ -->
  {% if page.next %}
  <li>
    <a href="{{ page.next.url }}">
    次の記事: {{ page.next.title }}
    </a>
  </li>
  {% endif %}
</ul>

年別アーカイブの前の年・次の年のリンクを得る…には努力が必要

年別アーカイブのページには今まで Explore ページのアーカイブ・リストへ戻るリンクを付けてましたが、そうではなくて今表示している年別アーカイブの、前の年や次の年へのリンクを付けたいと考えました。

しかし残念ながら Jekyll にはそのような機能は標準では付いていません。ということで、ないならないで作るしかありません。

年を引数にその前の年・次の年を探す Filter プラグインを作る

Jekyll の変数と既存の Filter だけを駆使してそういった機能を付けるのは厳しそうだったので、ここはプラグインを作ります。どういう感じにしようかと考えましたが、年をパラメーターに与えるとその前後の記事がある年をハッシュにして返してくれる Filter 1を作ることにしました。

次のような Ruby のコードを書いたファイルを _plugins ディレクトリに適当な名前で保存します。ファイル名は何でもいいですが、拡張子は .rb にしておくのがいいでしょう。あとコメントは何をやってるか分かりやすいように入れてるだけなので、削除しても構いません。

module Jekyll
  module GetArchivePaginator
    def get_archive_paginator(input)
      # input にはアーカイブページの年が入る
      # 後の作業のために整数に(※念のため)
      year  = input.to_i

      # @context:registers[:site] で site オブジェクトに
      # アクセスできる。posts はすべての記事が入っている
      posts = @context.registers[:site].posts

      # すべての記事を年別でグループ化
      # 年が key, 記事リストが value のハッシュが得られる
      # ちなみに posts.group_by だと警告が出ます…
      # (将来的に廃止予定らしい)
      group = posts.docs.group_by{|i| i.date.year}

      # 記事リストはいらないので年だけの配列にする
      # sort は単純に後の作業が感覚的に分かりやすいように
      years = group.keys.sort

      # 配列から現在の年の位置(インデックス)を検索
      current_index = years.index(year)

      # ひとつ前の年の処理
      # 現在の年が配列の最初の年と一緒ならそれより古い年は
      # ないので nil を、それ以外は現在のインデックスから
      # 1 を引いたインデックス位置の数値(年)を返す
      p = year != years.first ? years[current_index - 1] : nil

      # 同様にひとつ次の年の処理
      # 現在の年 = 配列の最後の年ならそれより新しい年はない
      n = year != years.last ? years[current_index + 1] : nil

      # あとは得られた変数からハッシュを作って返すだけ
      { "previous" => p, "next" => n }
    end
  end
end

# 最後はこの Filter を Liquid に登録するためのおまじない
Liquid::Template.register_filter(Jekyll::GetArchivePaginator)

この Filter で得られた変数を使って HTML を出力する

これでレイアウトファイル内に次の例にあるような Liquid タグを書くと archive_paginator に前後の年が入ったハッシュが入るので、あとは記事ページの時と同様に条件分岐で HTML を出力すればいいです。あっ a 要素のリンク URL は僕のサイトに合わせたものになってるので適宜変更してください。

<!-- これはアーカイブページの年を得るコード -->
{% assign year = page.date | date: "%Y" %}

<!-- ここからページネーションを出力するコード -->
{% assign archive_paginator = year | get_archive_paginator %}
<ul>
  <!-- 前の年 -->
  {% if archive_paginator.previous %}
  <li>
    <a href="/{{ archive_paginator.previous }}/">
    前の年: {{ archive_paginator.previous }} 年の記事一覧
    </a>
  </li>
  {% endif %}
  <!-- 次の年 -->
  {% if archive_paginator.next %}
  <li>
    <a href="/{{ archive_paginator.next }}/">
    次の年: {{ archive_paginator.next }} 年の記事一覧
    </a>
  </li>
  {% endif %}
</ul>

カテゴリー・ページは今までどおり(HTML を修正しただけ)

カテゴリー別のページ(ブログとライブ情報)は、最新記事 10 数件程度を抜粋したものと、全記事がずらっと並んだアーカイブ・ページの 2 種類がありまして、それらは各カテゴリーで進んだり戻ったりの関係にありますが、そのリンクはすでに両方のページに貼られています。すなわち

  • 最新記事のページには全記事のアーカイブ・ページへ「進む」リンクがあり
  • アーカイブ・ページには最新記事のページへ「戻る」リンクがある

という状態ですね。

これに関しては特に変更せず、そのまま使うことにしました。ただしマークアップは新しいナビゲーションに合わせたものに変更しております。

マークアップとスタイリング

マークアップの大枠は次のような感じです。

<nav class="pagination">
  <ul class="pagination-list">
    <!-- 前へのリンク用 -->
    <li class="pagination-prev">
      <a href="…" class="pagination-link _prev">…</a>
    </li>
    <!-- 次へのリンク用 -->
    <li class="pagination-next">
      <a href="…" class="pagination-link _next">…</a>
    </li>
  </ul>
</nav>

例によってレイアウトは2 CSS Grid です。スマホなどの狭い画面では項目が上下に、タブレット以上くらいの画面では左右に並びます。前後どちらかのリンクが存在しない場合でも CSS Grid だと grid-column プロパティで常に配置場所を指定しておけるので楽でいいですね。

@media (min-width: 35em) {
  .pagination-list {
    grid-template-columns: 1fr 1fr;
    /* repeat(2, 1fr) じゃない理由: 短い */
  }
  .pagination-prev {
    grid-column: 1 / 2;
  }
  .pagination-next {
    grid-column: -1 / -2;
  }
}

ちなみに grid-column の値をマイナスにすると、グリッドラインの後方から(最後のラインから最初のラインに向かって)位置を割り当てます。今回の次へ進むボタン(.pagination-next の要素)は構成要素の並びが右から左、テキストの寄せも右側なので、それに寄せる方向でこういう指定をしてみましたが、別に grid-column: 2 / 3 でも何の問題もないです。

以上、解説はこんなところですかね。マークアップもスタイリングもこれ以外の細かい部分は省略しておりますので、例によって興味のある方は開発者ツールなどで調べてください。ソースコードは全部 GitHub に Public で置いてあります

それ以外の細かな手直し

前後のナビゲーションとは直接関係ない話ですが、他にも一部コードの見直しとか不要になったファイルの削除などをしましたので、簡単に触れておきます。

日付の日本式フォーマット用に書いてた自作の Jekyll プラグインを削除
Liquid 標準の date filter でできるやん…ってことに今さら気が付きました
タグ・リスト用の SCSS パーシャルとタグ・アーカイブ用のテンプレートを削除
タグ付けをやめたので不要になったのに消してなかったので消しました
em 要素のスタイル・ルールをすっきりさせた
だいたいどんなブラウザでも text-emphasis が効くようになったので、古いブラウザ向けのフォールバックを削除しました
Explore ページにある年別アーカイブ・リストのスタイルを更新
display: table とか float とか使ってがんばってたのを CSS Grid Layout で全部書き直しました

…あたりがこの数日でやった作業。割と色々大変だったりするんですが、見た目的にはほとんど変化がないので、何がどう変わったのかさっぱり見当付かんのやないかと思います😅

そんな訳で

このサイトの各ページに前後を辿るナビゲーションを付けた際の作業メモでした。

で、実際に動くものはこのページだと下の方、関連記事のリストのすぐ下にとりあえず置いてみてるんですが、この場所ってどうなんだろう。問題ないとは思ってるんですけど、次に大きな Multiplex 広告があるので存在が分かりにくい感じもするしな…

その辺ちょっとよく分からないので詳しい人に訊いてみたい気もするけど、身近にそんな人いないのが悲しい。通りすがりの方でもいいので、誰かアドバイスください…😭

  1. これを Filter といっていいかというと厳密には違うと思いますが、適当に考えたやつなので許してください。涙 

  2. ダジャレ違うよ!