●pushStateとは?
Webアプリケーションを開発していると、ページ遷移をせずにURLを変更したいというシーンに遭遇することがあります。
そんな時に役立つのが、JavaScriptのpushState()メソッドです。
pushState()は、HTML5で導入されたHistory APIの一部で、ブラウザの履歴を操作することができます。
具体的には、現在のURLを変更し、ブラウザの履歴に新しいエントリを追加します。ただし、ページの再読み込みは行われません。
これによって、ユーザーはブラウザの戻る/進むボタンを使って、シームレスにページ間を移動できるようになります。
つまり、pushState()を使えば、Webアプリケーションのユーザーエクスペリエンスを向上させることができるのです。
○pushStateの基本的な使い方
では、実際にpushState()を使ってみましょう。
基本的な使い方は次のようになります。
第1引数のstateは、履歴エントリに関連付けられる状態オブジェクトです。
これは、popstateイベントが発生した際に、イベントオブジェクトのstate プロパティから取得できます。
第2引数のtitleは、現在のところ使用されていません。
将来的な使用に備えて空文字列を指定しておくのが一般的です。
第3引数のurlは、新しいURLを指定します。
これは、絶対パスでも相対パスでも構いません。
○サンプルコード1:シンプルなpushStateの使用例
ここでは、シンプルなpushState()の使用例を紹介します。
このコードを実行すると、URLが”/new-url”に変更されます。
ただし、ページの再読み込みは行われません。
また、ブラウザの戻るボタンを押すと、元のURLに戻ります。
これは、pushState()によって履歴エントリが追加されたためです。
○pushStateの引数について
pushState()の第1引数には、状態オブジェクトを指定します。
これは、popstateイベントが発生した際に、イベントオブジェクトのstate プロパティから取得できます。
状態オブジェクトは、シリアライズ可能なオブジェクトであれば何でも構いません。
例えば、次のようなオブジェクトを指定できます。
この状態オブジェクトは、後でpopstateイベントから取得することができます。
状態オブジェクトを使えば、URLに関連する情報をアプリケーション内で管理することができます。
これは、特にシングルページアプリケーション(SPA)を開発する際に役立ちます。
●pushStateとreplaceStateの違い
さて、ここまでpushState()について詳しく見てきましたが、実はHistory APIにはもう1つ、replaceState()というメソッドがあります。
一体、pushState()とreplaceState()の違いは何なのでしょうか?
結論から言うと、pushState()は新しい履歴エントリを追加するのに対し、replaceState()は現在の履歴エントリを置き換えるという違いがあります。
つまり、replaceState()を使うと、ブラウザの戻るボタンで前のページに戻ることができなくなるのです。
少しわかりにくいと思いますので、具体例を見ながら説明していきましょう。
○サンプルコード3:replaceStateの使用例
replaceState()の使用例を見ていきましょう。
このコードを実行すると、現在のURLが”/replaced-url”に置き換えられます。
ただし、新しい履歴エントリは追加されません。
そのため、ブラウザの戻るボタンを押しても、前のページには戻れません。
代わりに、その前のページに戻ることになります。
○pushStateとreplaceStateの使い分け
では、pushState()とreplaceState()はどのように使い分ければいいのでしょうか?
基本的には、次のように使い分けるのが一般的です。
- ページ遷移をシミュレートする場合は、pushState()を使う
- 現在のURLを変更したいだけの場合は、replaceState()を使う
例えば、シングルページアプリケーション(SPA)では、pushState()を使ってページ遷移をシミュレートすることが多いです。
一方、フォームのサブミット後にURLを変更したい場合などは、replaceState()を使うのが適しています。
ただし、replaceState()を使う場合は注意が必要です。
先ほども述べたように、replaceState()を使うとブラウザの戻るボタンで前のページに戻れなくなります。
これは、ユーザーにとって予期しない動作になる可能性があるからです。
そのため、replaceState()を使う場合は、ユーザーがブラウザの戻るボタンを使って前のページに戻りたいと思うようなシーンでは使わないようにしましょう。
代わりに、pushState()を使ってページ遷移をシミュレートするのが賢明です。
●popstateイベントについて
pushState()やreplaceState()を使ってURLを変更すると、ブラウザの履歴が変化します。でも、ページの再読み込みは発生しません。
じゃあ、URLが変わったことをJavaScriptで検知するにはどうすればいいのでしょうか?
その答えは、ズバリpopstateイベントです!
popstateイベントは、ブラウザの戻る/進むボタンが押されたときや、history.back()やhistory.forward()が呼び出されたときに発生します。
○サンプルコード4:popstateイベントの基本的な使い方
popstateイベントを使うと、ブラウザの履歴が変化したことを検知できます。
基本的な使い方は次のようになります。
このコードでは、window.addEventListenerを使ってpopstateイベントにイベントリスナーを登録しています。
イベントリスナーの中では、コンソールにメッセージを出力しているだけですが、実際のアプリケーションでは、URLが変わったタイミングで必要な処理を行うことになるでしょう。
event.stateには、pushState()やreplaceState()の第1引数で指定した状態オブジェクトが入ります。
これを使えば、URLに関連付けられた情報を取得することができます。
○popstateイベントの発火タイミング
ただし、popstateイベントが発生するタイミングには注意が必要です。
というのも、pushState()やreplaceState()を呼び出したタイミングでは、popstateイベントは発生しないからです。
popstateイベントが発生するのは、次のようなタイミングです。
- ブラウザの戻る/進むボタンが押されたとき
- history.back()やhistory.forward()が呼び出されたとき
- history.go()が呼び出されたとき(引数が0以外の場合)
つまり、JavaScriptでURLを変更しただけでは、popstateイベントは発生しません。ユーザーがブラウザの戻る/進むボタンを押すなどの操作をしてはじめて、popstateイベントが発生するのです。
○サンプルコード5:ブラウザの戻る/進むボタンを検知する
では、ブラウザの戻る/進むボタンが押されたことを検知するには、どうすればいいのでしょうか?
実は、これもpopstateイベントを使えば簡単に実現できます。
このコードでは、event.stateがnullかどうかで、ブラウザの戻る/進むボタンが押されたのか、それともpushState()またはreplaceState()が呼び出されたのかを判定しています。
ブラウザの戻る/進むボタンが押された場合、event.stateはnullになります。
一方、pushState()またはreplaceState()が呼び出された場合は、event.stateに状態オブジェクトが入ります。
●ブラウザの履歴を操作する
popstateイベントを使えば、ブラウザの履歴が変化したことを検知できることはわかりましたね。
でも、もっと積極的にブラウザの履歴を操作したいと思いませんか?
実は、History APIを使えば、JavaScriptからブラウザの履歴を自在に操作することができるんです。
これを使えば、例えば「戻るボタンを押したときに、特定のページまで一気に戻る」なんていうこともできちゃいます。
○サンプルコード6:履歴をさかのぼる
まずは、履歴をさかのぼる方法から見ていきましょう。
history.back()を使えば、ブラウザの戻るボタンを押したのと同じ効果が得られます。
これだけです。簡単ですね。でも、もっと柔軟に履歴を操作したいこともあるでしょう。
そんなときは、history.go()を使います。
history.go()は、引数に正の数を指定すると履歴を進み、負の数を指定すると履歴をさかのぼります。
上の例では、3つ前のページに戻っています。
○サンプルコード7:履歴を進める
履歴を進めるのも、history.go()を使えば簡単です。
この例では、2つ先のページに進んでいます。
history.forward()を使っても同じ効果が得られます。
ただし、履歴をさかのぼったり進めたりする場合は注意が必要です。
ユーザーが予期しない動作をすると、混乱を招く可能性があるからです。
必要な場面でのみ、慎重に使うようにしましょう。
○履歴エントリーの情報を取得する
最後に、履歴エントリーの情報を取得する方法を見ていきましょう。
history.state、history.length、location.hrefなどを使えば、現在の履歴エントリーに関する情報を取得できます。
これらの情報を活用すれば、より高度な履歴の操作が可能になります。
例えば、特定の状態オブジェクトを持つ履歴エントリーを探したり、現在のURLに応じて処理を切り替えたりできます。
ただ、そうすると履歴の操作がややこしくなってきますよね。
私は、履歴の操作は最小限にとどめ、ユーザーを混乱させないようにするのが賢明だと考えています。
●よくあるエラーと対処法
ここまで、pushStateやpopstateイベントについて詳しく見てきましたが、実際に使ってみるとエラーに遭遇することがあります。
そこで、よくあるエラーとその対処法を見ていきましょう。
○pushStateが効かない場合の対処法
まずは、pushStateが効かない場合の対処法です。
pushStateを使ってURLを変更しようとしたのに、URLが変わらないことがあります。
こんな感じのコードを書いたのに、URLが変わらない…。
実は、これにはいくつか原因が考えられます。
よくある原因は、サーバー側の設定が適切でないことです。
pushStateを使うと、URLが変わってもページの再読み込みは発生しません。
そのため、サーバー側でも適切な設定が必要になります。
例えば、Apacheの場合は、.htaccessファイルに次のような設定を追加する必要があります。
この設定がないと、pushStateで変更したURLにアクセスしたときに、404エラーが発生してしまいます。
もう1つの原因は、JavaScriptのコードにミスがあることです。
pushStateの使い方を誤っていると、URLが変わらないことがあります。
コードをよく確認して、ミスがないか確かめましょう。
○popstateイベントが発火しない場合の対処法
次は、popstateイベントが発火しない場合の対処法です。
popstateイベントを使って、ブラウザの戻る/進むボタンが押されたことを検知しようとしたのに、イベントが発火しないことがあります。
こんなコードを書いたのに、popstateイベントが発火しない…。
この場合も、いくつか原因が考えられます。
1つは、pushStateやreplaceStateを使っていないことです。
popstateイベントは、pushStateやreplaceStateを使ってURLを変更したときに発火します。
これを使っていない場合、popstateイベントは発火しません。
もう1つは、イベントリスナーの登録タイミングが遅いことです。
popstateイベントは、ページが読み込まれた直後に発火することがあります。
そのため、イベントリスナーの登録は、できるだけ早いタイミングで行う必要があります。
○ブラウザバックを禁止したい場合の対処法
最後は、ブラウザバックを禁止したい場合の対処法です。
ユーザーにブラウザの戻るボタンを押されたくない場面があります。
例えば、フォームの入力中などです。
こんな感じのコードを書いて、ブラウザバックを禁止しようとしたのに、うまくいかない…。
残念ながら、JavaScriptでブラウザバックを完全に禁止することはできません。
セキュリティ上の理由から、ブラウザの戻る/進むボタンの動作を変更することは許されていないのです。
ただし、代わりの方法はあります。
例えば、フォームの入力中にブラウザバックされると、入力内容が失われてしまうことを警告するメッセージを表示するのです。
この方法なら、ユーザーにブラウザバックの危険性を伝えることができます。
ただ、あまり乱用すると、ユーザーの操作性を損なってしまうので注意が必要です。
●pushStateの応用例
さて、ここまでpushStateやpopstateイベントの基本的な使い方について見てきましたが、実際のアプリケーション開発ではどのように活用されているのでしょうか?
実は、pushStateは特にシングルページアプリケーション(SPA)の開発で重宝されています。
SPAとは、1つのHTMLページで構成されるWebアプリケーションのことです。
ページ遷移が発生しないため、ユーザーエクスペリエンスが向上するのが特徴です。
そんなSPAの開発には、pushStateがうってつけなんです。
なぜなら、pushStateを使えば、ページ遷移なしでURLを変更できるからです。
これにより、SPAでもブックマーク可能なURLを実現できます。
では早速、具体的なサンプルコードを見ていきましょう。
○サンプルコード8:シングルページアプリケーション(SPA)での使用例
まずは、SPAでのpushStateの使用例です。
次のようなコードを書くことで、SPAでもブックマーク可能なURLを実現できます。
このコードでは、navigate関数を使ってページ遷移をシミュレートしています。
具体的には、次の処理を行っています。
- コンテンツを更新する(updateContent関数)
- history.pushStateを使ってURLを変更する
また、popstateイベントを監視することで、ブラウザの戻る/進むボタンが押されたときにもコンテンツを更新しています。
このようにすることで、SPAでもブックマーク可能なURLを実現できるのです。
ただ、サーバー側の設定も必要になるので注意が必要です。
○サンプルコード9:ReactでのpushStateの使用例
続いて、ReactでのpushStateの使用例を見ていきましょう。
Reactは、FacebookによるJavaScriptのライブラリで、UIの構築に特化しています。
ReactでpushStateを使う場合は、react-routerというライブラリを使うのが一般的です。
ここでは、react-routerを使ったサンプルコードを紹介します。
このコードでは、react-routerのBrowserRouterコンポーネントを使って、pushStateベースのルーティングを実現しています。
Linkコンポーネントを使って、ページ遷移のリンクを作成しています。
また、Routeコンポーネントを使って、URLに応じてコンポーネントを切り替えています。
まとめ
JavaScriptのpushStateとpopstateイベントを使いこなすことで、ページ遷移なしでURLを変更し、ブラウザの戻る/進むボタンでシームレスにページ間を移動できます。
また、ブラウザの履歴の変化を検知し、より洗練されたWebアプリケーションを開発することができるでしょう。
ただし、使い方を誤ると予期せぬ動作を引き起こす可能性もあるので、十分な注意が必要です。
適切に使いこなすことで、ユーザーエクスペリエンスの向上につなげましょう。
本記事で紹介したサンプルコードを参考に、pushStateとpopstateイベントを活用してみてください。
より高度なWebアプリケーションの開発にチャレンジすることで、スキルアップにつながるはずです。