ページ遷移を伴わない、jsで作成した一覧で、ページ送りしたときクエリパラメータ?page=n
をつけたほうが良いというお話です。ブックマークなどから再アクセス可能で便利ですし、リロードやブラウザバックで1ページ目にリセットされてしまうような問題も発生しなくなります。ページネーションだけでなく、検索機能がある場合も同様です。
最近SPAをよく作るのですが、違う情報を表示しているならURLパスも変える、というUI上当然のことを忘れがちになっているので…。
ということでvue.jsでそのような実装サンプルを作成しました。routerは利用しない例です。
要件
- 一覧ページと詳細ページがある
- 一覧のリンクをクリックすると詳細ページに遷移する
- 一覧はvue.jsで作成
- ページを切り替えると、一覧部分のみjsで書き換える
- データは1ページずつDBからロードする
デモ
URLに注目してみてみてください。

⬇実際に動作するデモはこちら。
⬇ソースコードはこちら。
実装方法
ページを切り替えたときに?page=n
というクエリパラメータを付加し、historyオブジェクトにクエリパラメータも含めたURLを保存します。?page=n
つきで再アクセスされた場合は、もちろん該当ページを取得して表示します。
vue.jsで実装していますが、この方法はプレーンなjsでもjQueryでも応用できると思います。
たとえば2ページ目をブックマークして再アクセスも可能になるのでオススメです。
実装方法
一覧とページネーションの、HTMLの実装です。
<div id="vueList" :class="searching ? 'loading' : ''">
<table class="osare4-table">
<thead>
<tr>
<th style="width: 100px">NO</th>
<th style="width: 200px">NAME</th>
<th style="width: 200px">ENG</th>
</tr>
</thead>
<tr v-for="country in countries">
<td>{{country.id}}</td>
<td><a href="detail.html">{{country.name}}</a></td>
<td>{{country.eng}}</td>
</tr>
</table>
<ol class="pagenation">
<li @click="setPage(1)"><i class="fas fa-angle-double-left"></i></li>
<li @click="setPage(page - 1)"><i class="fas fa-angle-left"></i></li>
<li v-for="n of totalPage" :key="n" @click="setPage(n)" class="pagenation_num" :class="n === page ? 'active' : ''">{{n}}</li>
<li @click="setPage(page + 1)"><i class="fas fa-angle-right"></i></li>
<li @click="setPage(totalPage)"><i class="fas fa-angle-double-right"></i></li>
</ol>
</div>
vue.jsの実装です。
const PAGE_PER = 10;
new Vue({
el: "#vueList",
data: {
countries: [],
page: 1,
totalPage: 10,
searching: true,
},
mounted() {
const page = getParam("page");
if (page != null && !isNaN(page)) {
this.page = Number(page);
}
this.fetch();
},
watch: {
page() {
const url = new URL(location);
if (this.page > 1) {
url.searchParams.set("page", this.page);
} else {
url.searchParams.delete("page");
}
history.replaceState("", "", url.toString());
this.fetch();
},
},
methods: {
fetch() {
// ここでajaxでバックエンドからデータを取得した方が良い
this.searching = true;
setTimeout(() => {
let start = PAGE_PER * (this.page - 1);
this.countries = countries.slice(start, PAGE_PER * this.page);
this.searching = false;
}, 1000);
},
setPage(page) {
this.page = page;
},
},
});
function getParam(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return "";
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
ページ切替時の処理
ポイントとなるのは、vue.jsのwatchの部分。これはページネーションでページを切り替えたときに呼び出される処理です。
watch: {
page() {
const url = new URL(location);
if (this.page > 1) {
url.searchParams.set("page", this.page);
} else {
url.searchParams.delete("page");
}
history.replaceState("", "", url.toString());
this.fetch();
},
},
const url = new URL(location);
⬆URLというオブジェクトを生成しています。URLをいろいろ操作できるオブジェクトです。
if (this.page > 1) { url.searchParams.set("page", this.page); } else { url.searchParams.delete("page"); }
⬆URLオブジェクトのsearchParams
はクエリパラメータを操作することができます。1ページ目を超える場合は?page=n
をURLに付与し、1ページ目の場合は?page=n
をURLから削除しています。
history.replaceState("", "", url.toString());
⬆ここが最重要。historyオブジェクトにURLを保存しています。こうすることで、2ページ目を表示して、詳細ページに遷移し、ブラウザバックした場合でも?page=2
に戻ってこれるようになります。
this.fetch();
⬆これは一覧を再取得するメソッドを呼び出しています。この部分の詳細を続けて説明していきます。
一覧を取得する処理
デモなのでこのコードは参考にならないですが、現在ページのデータをバックエンドから取得する処理を記述します。ajaxで実装が必要です。
※このデモではjs配列の表示する部分を切り出しているだけです。このような実装だと最初に全件ロードが必要なのでデータが大量にある場合に不向きです。
fetch() {
// ここでajaxでバックエンドからデータを取得した方が良い
this.searching = true;
setTimeout(() => {
let start = PAGE_PER * (this.page - 1);
this.countries = countries.slice(start, PAGE_PER * this.page);
this.searching = false;
}, 1000);
},
this.searching = true;
⬆これはローディングを表示するための処理です。ローディングは、ユーザーインタフェースを考慮すると実装するべきでしょう。
Nメージ目でアクセスされたときの初期ロード処理
vue.jsのmountedは画面初期表示時、DOM構築後に呼び出される処理です。
mounted() {
const page = getParam("page");
if (page != null && !isNaN(page)) {
this.page = Number(page);
}
this.fetch();
},
const page = getParam("page");
⬆クエリパラメータの?page=n
のnを取得するコードです。getParam関数は別途定義しています。コード全体の方をご確認ください。
if (page != null && !isNaN(page)) { this.page = Number(page); }
⬆クエリパラメータの?page=n
が設定されていて、nが数字の場合に、カレントページを切り替えています。もしもクエリパラメータに?page=n
がない場合は1ページ目を表示します。また、nにa
など数字でない文字が設定された場合も1ページ目になります。
this.fetch();
⬆これは上記で説明した一覧を再取得するコードです。
その他の実装方法
vue.jsの場合、routerが使えるのなら使わない手はありません!
ブラウザバックしてもページネーションを維持させる方法 | カバの樹
こちらはjquery.pajinateを利用した例で、本記事で紹介しているのと似た方法です。
JSで切り替えたページにBackで戻れるようにする – Qiita