「前のページに戻る」「1つ進む」ボタンをページ内に置きたいとき、JavaScriptのhistory オブジェクトを使えば数行で実装できます。ブラウザのツールバーにある戻る/進むボタンと同じ動きを、自分のボタンで再現できます。
基本は history.back() と history.forward() だけですが、実務では「戻り先が無いときどうするか」「SPAでの戻る対応」などつまずきやすい点があります。この記事ではそこまで含めてそのまま動くコードで解説します。
history.back()、進むは history.forward()、複数ページの移動は history.go(n)(負で戻る/正で進む)で実装します。ただし「戻り先があるか」をJavaScriptで正確に判定する方法はありません。履歴が無いときはトップページへ飛ばすなどのフォールバックを用意します。戻る・進むボタンの基本実装
ボタンにイベントを割り当て、クリックで history.back() / history.forward() を呼ぶだけです。window.history は単に history と書いても同じものを指します。
<button id="back">戻る</button> <button id="forward">進む</button>
// HTMLとロジックを分けるため addEventListener で割り当てる
document.getElementById("back").addEventListener("click", () => {
history.back(); // 1つ前の履歴へ
});
document.getElementById("forward").addEventListener("click", () => {
history.forward(); // 1つ先の履歴へ
});
古い記事では <button onclick="goBack()"> のようにHTML属性へ直接書く例も見かけますが、現在は上のように addEventListener でJavaScript側にまとめるのが一般的です。
history.go(n)で複数ページ移動する
history.go() に整数を渡すと、その分だけまとめて移動できます。back() / forward() は go(-1) / go(1) と同じです。
history.go(-1); // 1つ戻る(back() と同じ) history.go(1); // 1つ進む(forward() と同じ) history.go(-2); // 2つ前のページへ一気に戻る history.go(0); // 現在のページを再読み込み(リロード)
history.go(0) と引数なしの history.go() は現在ページのリロードになります。移動のつもりで 0 を渡さないよう注意してください。「戻れるか・進めるか」は判定できない
「戻り先が無いときはボタンを無効化したい」と考えがちですが、ブラウザには「戻れるか」を調べる標準APIがありません(canGoBack のようなものは存在しません)。プライバシー保護のための仕様です。
history.length はそのタブの履歴件数を返しますが、他サイトを経由した分も含まれ、現在位置が何番目かは取得できません。 「進める履歴があるか」の判定には使えないと考えてください。また、履歴の先頭(最初に開いたページ)で history.back() を呼んでも何も起きません。ボタンが効かないように見えるため、履歴が無いときはトップページなどへ飛ばすフォールバックが有効です。
document.getElementById("back").addEventListener("click", () => {
if (history.length > 1) {
history.back(); // 履歴があれば戻る
} else {
location.href = "/"; // 直接開かれた場合などはトップへ
}
});
これも完全な判定ではありませんが(history.length の制約があるため)、「直接URLを開いて戻り先が無い」ケースの保険としては実用的です。
popstateで履歴の移動を検知する
戻る・進むで履歴が切り替わったタイミングを検知したいときは popstate イベントを使います。back() / forward() / go() やブラウザのボタン操作で発火します。
window.addEventListener("popstate", (event) => {
console.log("履歴が移動しました");
console.log(event.state); // pushState で保存した状態(無ければ null)
});
なお pushState() / replaceState() を呼んだだけではpopstate は発火しません。あくまで履歴の移動(戻る・進む)のときだけ呼ばれます。
SPAでの「戻る」対応(History API)
ページ全体を再読み込みしないSPA(シングルページアプリ)では、history.pushState() でURLと状態を履歴に積み、popstate で戻る・進むに合わせて画面を復元します。
// 画面遷移のたびに状態とURLを履歴へ追加
history.pushState({ view: "detail", id: 12 }, "", "/items/12");
// 戻る・進むで呼ばれ、保存した状態から画面を復元
window.addEventListener("popstate", (event) => {
const state = event.state;
if (state) renderView(state); // 例: 状態に応じて再描画
});
Vue RouterやReact Routerなどのルーターは、この仕組みを内部で自動的に扱っています。なお「戻るボタンを押させたくない(無効化したい)」という逆の要望と、なぜ完全には無効化できないのかはブラウザの戻るボタンを無効化する方法で詳しく解説しています。
history による移動と location の違い
history.back() などは履歴の中を行き来する操作で、移動先のURLを指定することはできません。一方、特定のURLへ移動したい場合は location.href = "..." を使います。こちらは新しい履歴エントリを追加して指定したページへ遷移します。
- 戻る・進む(履歴の前後へ)→
history.back()/forward()/go(n) - 特定URLへ移動→ URLを遷移させる方法(
location)
現在のURLそのものを取得したいときは現在のURLを取得する方法も参考になります。
よくある質問(FAQ)
history.back() で戻る、history.forward() で進む、history.go(n) でn段階の移動ができます(負で戻る/正で進む)。履歴の移動は popstate イベントで検知して追加処理を行えます。canGoBack のような標準APIは無く、history.length も他サイト経由分を含み現在位置が分からないため当てになりません。履歴が無いときは location.href = "/" でトップへ飛ばすなどのフォールバックで対応します。history.pushState() でURLと状態を履歴に積み、popstate イベントでルーティング処理を行ってページ状態を復元します。Vue RouterやReact Routerはこれを自動で処理します。history.pushState({ data: state }, "", url) でカスタムデータを履歴に保存し、popstate の event.state から取り出して画面を復元します。まとめ
ページ内の戻る・進むボタンは、history.back() / history.forward() / history.go(n) の3つで実装できます。go(0) はリロードになる点に注意してください。
ただし「戻り先があるか」をJavaScriptで正確に知ることはできないため、履歴が無いときはトップへ飛ばすフォールバックを用意しておくと親切です。SPAで戻る・進むを正しく扱うなら pushState と popstate を組み合わせ、特定URLへの移動が目的なら location を使い分けましょう。

