第22回 記事一覧の上部にもページナビを追加―10件表示を上下から移動できるようにする
記事一覧、カテゴリ、サブカテゴリ、連載詳細で、一覧末尾だけにあったページナビを上部にも追加しました。上下のナビを共通コンポーネント化し、同じpage・sort状態を使うようにした内容を記録します。

第21回では、記事一覧・カテゴリ・サブカテゴリ・連載詳細を1ページ10件に変更し、「新しい順 / 古い順」とページ移動を追加した。
ページを分けたことで、30件を超えた記事を1ページに全部並べる状態は解消した。一方で実際の画面を使っていると、もう1か所気になるところが出てきた。
ページ移動の「最初 / 前へ / 1 / 4 / 次へ / 最後」が一覧の末尾にしかない。
1ページ10件なら大きな問題にはならないが、2ページ目へ進みたいだけでも毎回10記事分を下までスクロールする必要がある。記事がさらに増えても同じ動きになるので、今回はページナビを一覧上部にも追加する。
修正前は並び順だけが上部にあった#
修正前の記事一覧では、上部にある操作は「並び順」だけだった。

ページ移動は記事を10件読み進めたあと、一覧末尾まで来たところで表示される。

第21回で追加したページネーションそのものは正常に動いているので、今回はページ数の計算や?page=の仕組みを作り直す必要はない。
変更したいのは配置だけになる。
並び順
ページナビ
↓
記事 10件
↓
ページナビ
上部と下部のどちらからでも同じページへ移動できる形にする。
ページナビをPaginationNavへ切り出す#
修正前はPaginatedArticleList.astroの中にページナビのHTMLが直接あり、下部に1回だけ出力していた。
このまま同じHTMLを上にもコピーすれば見た目だけは実現できる。ただ、ページリンクの生成やdisabled状態を2か所で持つと、あとで片方だけ修正して表示がずれる可能性がある。
そこでページナビ部分を新しいPaginationNav.astroへ切り出した。
PaginatedArticleList.astroからは、同じ現在ページと総ページ数を渡して上下2回呼び出す。
PaginatedArticleList
├─ PaginationNav position="top"
├─ 記事一覧
└─ PaginationNav position="bottom"
上部と下部は別々のページ状態を持たない。
共有しているのは次の値と処理になる。
- current page
- sort
?page=?sort=- 最初 / 前へ / 次へ / 最後のdisabled状態
- ブラウザの戻る / 進む
- 上下に表示する現在ページ番号
上部のdata-pageリンクを操作しても、下部を操作しても、最終的に同じ状態へ更新される。
上部ページナビを並び順の下へ追加#
修正後の/blog/では、並び順の下にページナビが表示されるようになった。

作業時点の記事数は33件なので、1ページ10件で4ページになる。
1ページ目では、先頭側の「最初」と「前へ」は無効。次へ進める「次へ」「最後」はリンクとして使える。
ページナビをソートの横へ無理に詰め込まず、その下へ置いた。横幅が狭くなっても並び順とページ移動が干渉しにくい。
2ページ目でも上下の状態をそろえる#
上部ナビから2ページ目へ移動すると、上部の表示は2 / 4へ変わる。

同じページの末尾まで移動すると、下部も2 / 4になっている。

上下を別々に実装したのではなく、同じPaginatedArticleListの状態から両方を更新しているので、片方だけ1ページ目のまま残ることはない。
?page=と?sort=の扱いも第21回のまま維持している。
?page=2
?sort=oldest
?sort=oldest&page=2
ソートを変更した場合は1ページ目へ戻る。不正なページ番号は有効範囲へ補正する。ブラウザの戻る・進むでも表示を復元する。
今回の変更で、URLの形式は増やしていない。
カテゴリとサブカテゴリにも同じナビを使う#
上部ページナビは/blog/だけに追加したわけではない。
PaginatedArticleListを使用しているカテゴリとサブカテゴリにも同じ変更が入る。
たとえば「WEB制作 > 運用・改善」は作業時点で13記事あるため、10件と3件の2ページになる。

カテゴリ説明、記事件数、並び順はそのまま残し、その下に上部ページナビを追加している。
ページが1つしかない分類では、上下ともページナビを表示しない。記事が少ないページに「1 / 1」だけの操作を増やさないようにした。
作業時点の主なページ数は以下。
| 対象 | 記事件数 | ページ数 |
|---|---|---|
| 記事一覧 | 33件 | 4ページ |
| WEB制作 | 26件 | 3ページ |
| 運用・改善 | 13件 | 2ページ |
| Astro+Cloudflareでサイトを作る | 22件 | 3ページ |
記事公開後は件数が増えるため、この数値は実装時点のものになる。ページ数自体は記事数から自動計算される。
連載ページでも第1回から読む順序を維持する#
連載詳細にも同じ上部ページナビを追加した。

ここは通常の記事一覧とソート基準が違う。
前回修正したとおり、/blog/、カテゴリ、サブカテゴリはpubDate、連載詳細はseriesOrderを使う。
/blog/
カテゴリ
サブカテゴリ
新しい順 → pubDate 降順
古い順 → pubDate 昇順
連載詳細
古い順 → seriesOrder 昇順
新しい順 → seriesOrder 降順
今回ページナビを共通化しても、このルールは変更していない。
「Astro+Cloudflareでサイトを作る」で古い順を選ぶと、1ページ目はseriesOrder 1~10、2ページ目は11~20、3ページ目は21~22になる。新しい順では逆方向から表示する。
番外編を含む内部順序もこれまでどおりseriesOrderで管理する。
上下のnavには別のaria-labelを付ける#
同じページにページナビが2つ存在するため、HTML上では位置を区別できるようにした。
上部は、
aria-label="上部ページナビゲーション"
下部は、
aria-label="下部ページナビゲーション"
を使う。
現在ページには既存のaria-current="page"を維持している。
見た目は同じページナビでも、支援技術側からは上部と下部を区別できるようにした。
ビルドと表示を確認する#
今回変更した主なファイルは2つ。
src/components/PaginationNav.astro
src/components/PaginatedArticleList.astro
実装中、新しく作ったPaginationNav.astroでAstroのフロントマター区切りが不足し、一度ビルドエラーになった。区切りを修正したあと、再度ビルドと検証を行っている。
実行したコマンドは下記。
npm run build
node scripts/verify.mjs
git diff --check
astro preview --background --host 127.0.0.1
最終ビルドは成功し、117ページを生成した。
Pagefindは33ページ、3,023語を対象に生成。RSS、sitemap、タグページのnoindex、BreadcrumbList JSON-LDも既存の状態を維持している。
ブラウザでは上下ナビのページ状態、?page=、?sort=、連載の古い順・新しい順を確認した。
レスポンシブ確認では、使用したヘッドレスブラウザの制約で320~390px指定時の実測viewportが500pxになった。そのため500pxでの実表示と、max-width: 520px / 720pxのCSS条件を確認している。320pxで実測したとは扱わない。
上下ページナビの変更点#
第21回で追加した10件表示の仕組みはそのまま残し、ページ移動の入口だけを上下へ増やした。
- ページナビを
PaginationNav.astroへ共通化 - 一覧上部にも「最初 / 前へ / 現在ページ / 次へ / 最後」を追加
- 下部ページナビはそのまま維持
- 上下でcurrent page、sort、URL queryを共有
- 1ページだけの一覧では上下とも非表示
/blog/、カテゴリ、サブカテゴリ、連載詳細へ反映- 連載の
seriesOrder基準は変更しない
これで、2ページ目以降へ移動するために毎回10記事分を下までスクロールする必要はなくなった。

