ページネーション
Pagination
概要(サマリー)
ページネーションとは、大量の一覧データを複数のページに分割し、1ページずつ表示する仕組みのことである。検索結果の下にある「1 2 3 … 次へ」のリンク群を思い浮かべればよい。
分厚い辞書を1枚の紙に印刷しないのと同じで、100件も1000件もあるデータを一度に出すのは、読む側にも表示する側にも無理がある。そこで「1ページ20件」のように区切り、必要なぶんだけ渡すのがページネーションである。
以下は、5ページ分のページ送りを実際に動かせる簡単なデモである。
1ページ目を表示中(1〜20件目 / 全100件)
詳細解説
ページネーションとは何か
ページネーションは「全体をいくつに割り、いま何番目を見ているか」を管理する仕組みである。必要な情報は次の3つだけだ。
- 1ページあたりの件数(例: 20件)
- 全体の件数(例: 100件)
- 現在のページ番号(例: 3ページ目)
この3つが決まれば、「何件目から何件目を出すか」も「全部で何ページあるか」も計算で求められる。3ページ目・1ページ20件なら、41件目から60件目を表示すればよい。総ページ数は、全体件数を1ページあたりの件数で割って切り上げた数になる。
なぜページを分けるのか
一覧を全件まとめて出すと、次の問題が同時に起きる。
- 表示が重くなる。1000件のカードを描画すればブラウザのレンダリングに時間がかかる
- 通信量が増える。読まれない900件ぶんのデータまで転送することになる
- サーバーの負荷が上がる。データベースから全件を取り出すコストは件数に比例する
- 読む側が探せない。目的の1件までスクロールし続けるのは苦痛である
ページを分けると、これらがすべて「1ページぶん」に縮む。ページネーションは見た目の話に見えて、実は性能の話でもある。
「何ページ目か」をどう伝えるか
現在のページ番号は、多くの場合URLに載せる。よく使われるのはクエリパラメータである。
https://example.com/blog/?page=3
https://example.com/blog/page/3/
上は ?page=3 というクエリパラメータ、下はパスにページ番号を含める書き方だ。どちらでもよいが、URLにページ番号が載っていることが重要になる。URLに載っていれば、3ページ目をブックマークでき、共有もでき、ブラウザの戻るボタンでも正しく戻れる。
逆に、JavaScriptだけでページを切り替えてURLが変わらない作りにすると、リロードしただけで1ページ目に戻ってしまう。
サーバー側での取り出し方
SQLでページ分割するときは、「何件取るか」と「何件飛ばすか」の2つを指定する。次は3ページ目(1ページ20件)を取り出す例である。
SELECT title, created_at
FROM articles
ORDER BY created_at DESC
LIMIT 20 OFFSET 40; -- 20件取り出す。先頭から40件は飛ばす
OFFSET 40 が「41件目から」を意味する。注意したいのは、ORDER BY で並び順を固定しないと結果が安定しないことだ。並び順が毎回変わると、同じ記事が2ページ目にも3ページ目にも出たり、逆にどのページにも出なかったりする。
ページ送り以外の見せ方
ページ番号を並べる形だけがページネーションではない。用途によって次の方式が使い分けられる。
- 番号型: 「1 2 3 … 12」と並べる。何ページあるか一目で分かり、特定のページへ飛べる。記事一覧や検索結果に向く
- もっと見る型: ボタンを押すと続きが下に追加される。読む流れを止めずに済む
- 無限スクロール型: 下端に近づくと自動で続きを読み込む。SNSのタイムラインなど、終わりを意識させたくない場面に向く
無限スクロールは快適だが、フッターにたどり着けない、目的の位置に戻れない、といった弱点がある。「探す」ページには番号型、「眺める」ページには無限スクロール型、と考えると選びやすい。
実装時に気をつけること
- 現在ページを色だけで示さない。色覚特性によっては区別できないため、
aria-current="page"を付けて支援技術にも現在位置が伝わるようにする。これはアクセシビリティの基本である - 前へ・次へを端で止める。1ページ目で「前へ」が押せると、0ページ目という存在しない場所へ飛んでしまう
- タップ領域を確保する。スマートフォンで数字が小さすぎると押し間違える。ユーザビリティに直結する
- 総ページ数が多いときは省略する。100ページぶんの数字を並べても使えない。「1 2 3 … 100」のように省略記号でたたむ
NOVEBLOには、ページネーションの実装手順とデザイン見本をまとめたページがある。
SEO上の扱い
ページ2以降も検索エンジンにクロールされる独立したページである。ここでよくある事故が、2ページ目以降のcanonicalを1ページ目に向けてしまうことだ。こうすると2ページ目以降の中身が評価されなくなり、そこにしかない記事が検索結果に出づらくなる。
原則として、各ページのcanonicalはそのページ自身に向ける。また、「次へ」「前へ」は検索エンジンがたどれる通常のリンク(<a href="...">)で置くとよい。パンくずリストと同じく、リンクとして機能していることが前提になる。
AIコーディングとの関係
ページネーションは定型パターンなので、AIは得意である。ただし指示が曖昧だと、見た目だけそれらしいものが返ってくる。次の4点を具体的に伝えるとよい。
- 総件数と1ページあたりの件数(例: 全100件・1ページ20件)
- どこにページ番号を持たせるか(URLのクエリパラメータか、画面内の状態だけか)
- 省略表示の有無(総ページ数が多い場合に「…」でたたむか)
- 現在ページの示し方(
aria-currentを付けるか)
たとえば「全100件を1ページ20件で表示するページネーションを作って。現在ページはURLの ?page= で管理し、現在位置には aria-current="page" を付けて。前へ・次へは端で無効化して」と伝えると、意図に近いものが出てくる。
生成されたコードで特に確認したいのは、端の処理と総ページ数の計算である。1ページ目で「前へ」が押せてしまう、総ページ数の切り上げを忘れて最後の数件が表示されない、といった境界のずれはAIが取りこぼしやすい。最初のページ・最後のページ・件数が1ページに満たない場合の3つを、必ず自分で試してほしい。
よくある勘違い
ページネーション = 見た目のパーツ?
数字のボタンだけがページネーションだと思われがちだが、本質は「データを何件ずつ切り出すか」という取り出し方の設計にある。ボタンを置いても、サーバーから全件受け取って画面で隠しているだけなら、通信量もサーバー負荷も減らない。表示側と取り出し側はセットで考える必要がある。
無限スクロールならページ番号は不要?
見た目にページ番号が出ないだけで、裏側では「何件目から何件取るか」を必ず管理している。そのため無限スクロールもページネーションの一種と考えてよい。むしろ、URLに位置が残らないぶん、戻ったときの復元は自前で作り込む必要がある。
2ページ目以降は検索に出なくてよい?
2ページ目以降にしかない記事は、そこからしかたどれないことがある。まとめてcanonicalを1ページ目に向けたり、noindex を付けたりすると、その記事の入口を自分でふさぐことになる。分割したページも独立したページとして扱うのが原則である。
OFFSETは何件でも同じ速さ?
OFFSET 40 なら40件を読み飛ばすだけだが、OFFSET 100000 になると、データベースは10万件を数えてから捨てる動きになりやすい。件数が非常に多い一覧では、ページ番号ではなく「前回の最後のID以降を取る」方式のほうが速いことがある。
まとめ
- ページネーションは、大量の一覧を複数ページに分割して1ページずつ表示する仕組みである
- 「1ページあたりの件数」「全体件数」「現在ページ」の3つが分かれば、表示範囲と総ページ数は計算で求められる
- 現在ページはURLに載せる。載せておくとブックマーク・共有・戻る操作が正しく働く
- SQLでは
LIMITとOFFSETで切り出す。ORDER BYで並び順を固定しないと、抜けや重複が起きる - 2ページ目以降も独立したページとして扱い、canonicalは各ページ自身に向ける
情報ソース
より詳しくAIに聞いてみよう
- ページネーションとは何かを、初心者にもわかるように具体例つきで説明してください。
- ページネーションと無限スクロールの違いを、それぞれが向いている場面とあわせて整理してください。
- SQLの LIMIT と OFFSET でページ分割するとき、件数が増えると遅くなる理由と対策を教えてください。
- ページネーションでアクセシビリティ上気をつけるべき点を、具体的なHTMLの例つきで教えてください。
- AIにページネーションのコードを生成してもらうとき、境界(最初と最後のページ)の不具合を防ぐにはどう指示すればよいですか。