2016/04/27
pushState() + AJaxでスムーズなコンテンツの遷移
Ajaxはページ遷移で発生するユーザーのストレスを軽減する方法として有効ですが、
ページ遷移の履歴が残らないため、
ブラウザの戻るボタンを押した場合に、ユーザーの意図しない動作になってしまう場合が多々あります。
HTML5のJavaScriptの仕様で、履歴を扱うhistoryオブジェクトに、
pushState()およびpopstateイベントが追加され、
Ajaxのような実際にページ遷移しない場合でも、ページの変化があるごとに、URLを変更して履歴を追加することができるようになりました。
この機能はIE10以上のブラウザで利用することができます。
今回は、この機能を使ったページ遷移の方法をご紹介いたします。
↓作ってみたもの
方法
- 遷移するそれぞれのページを、Ajax経由ではなく、直接開いても問題ない状態で用意する
- 初期で読み込んだページのURLを取得し、
pushState()→ replaceState()で最初のページの履歴を作成しておく(2017年6月28日訂正) - リンクを押した場合に、pushSate()でURLを変更し、Ajaxで遷移先のページの情報を読み込み、部分的に読み込む
- ブラウザの戻るボタンが押された場合に、popstateイベントが発生するので、変更前の状態に変更する
1. 遷移するそれぞれのページを、Ajax経由ではなく、直接開いても問題ない状態で用意する
Ajaxで読み込まれるページは、
直接URLをブラウザで辿っても、Ajaxで読み込んだ場合と同じように表示されるような作りにしておきます。
2. 初期で読み込んだページのURLを取得し、pushState() → replaceState()で最初のページの履歴を作成しておく(2017年6月28日訂正)
pushState()でURLを変更した場合、Ajaxの読み込みなど、ページ変化の情報は基本的にブラウザに残りません。
そのため、戻るボタンで戻った時の状態は、別で再現する必要があります。
その仕組として、pushState()でURLを変更するときに、stateオブジェクトという任意の情報を残しておき、
戻るボタンが押されたときに発生するpopstateイベントの情報として、残しておいたstateオブジェクトを読み込み、
その情報を元に戻った時の状態を再現するという方法を取ります。
1 2 3 4 5 6 7 8 9 10 11 12 | var state = { "targetPageUrl": 「ページのURLなど」 }; history.pushState(state, 「任意のタイトル(現状のブラウザでは意味を持たない)」, 「変更後のURL」); //ページのコンテンツををAjaxなどで変更 ・・・ window.addEventListener('popstate', function(e) { var state = e.state; if(state) { // ページのコンテンツを変更前の状態に再現 } }); |
一番最初、ページが読み込まれたときは、pushState()によってページの情報が残されていないため、
戻るボタンで最初に戻ったときに、再現ができなくなってしまう問題が発生します。
そのため、ページが読み込まれた時点で、pushState() → replaceState()を実行して、最初の状態のページ情報を残すようにします。
1 2 3 4 5 6 | var currentPageUrl = location.pathname; currentPageUrl = currentPageUrl.split("/"); currentPageUrl = currentPageUrl[currentPageUrl.length - 1]; var state = {"targetPageUrl": currentPageUrl}; history.replaceState(state, currentPageUrl.split(".")[0], currentPageUrl); |
追記(2017年6月28日)
pushState()で最初の情報を残すと、情報だけでなく履歴も追加してしまうため、動作がおかしくなってしまうようです。最初の情報を残すときは、pushState()ではなく、replaceState()を使うのが良いようです。
参考サイト:
HTML5 History API を徹底的に試してみる | nacika’s Web Programming Blog
3. リンクを押した場合に、pushSate()でURLを変更し、Ajaxで遷移先のページの情報を読み込み、部分的に読み込む
Ajaxによるページ遷移を実装します。
Ajaxによる変更は、通常通り行いますが、変更の際にpushState()の処理を行い、
実際にはページ遷移しないのですが、URLだけを変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | $("nav a").click(function() { var $currentElm = $("section.active"), targetPageUrl = $(this).attr("href"), pageId = targetPageUrl.split(".")[0], state = {"targetPageUrl": targetPageUrl}; history.pushState(state, pageId, targetPageUrl); //URL変更 $("nav a").removeClass("active"); $(this).addClass("active"); changeContent($currentElm, targetPageUrl, pageId); //ページ変更の処理は関数にまとめておく return false; }); |
1 2 3 4 5 6 | function changeContent($currentElm, targetPageUrl, pageId) { //jQueryのload()を使ってAjaxによるページの読み込みを行う $wrapper.find("section").last().load(targetPageUrl + " " + elmSelector, function(a, b, c) { //変更の処理を行う }); } |
この時のポイントは、
変更する処理を関数にまとめておき、
戻るボタンが押された時にも同様の処理が行えるように準備しておくことです。
4. ブラウザの戻るボタンが押された場合に、popstateイベントが発生するので、変更前の状態に変更する
ページ変更のときに使った関数を流用し、
戻るボタンが押されたときに発生するpopstateイベントのタイミングで、
変更前の状態にページを変更します。
1 2 3 4 5 6 7 8 9 10 11 12 | $(window).on('popstate', function(e) { var state = e.originalEvent.state; //pushState()で渡しておいたstateオブジェクトを取得する if(state) { var $currentElm = $("section.active"), targetPageUrl = state.targetPageUrl, pageId = targetPageUrl.split(".")[0]; $("nav a").removeClass("active"); $("nav a[href='" + targetPageUrl + "']").addClass("active"); changeContent($currentElm, targetPageUrl, pageId); //変更するときの関数を流用 } }); |
Author Profile
NINOMIYA
Webデザイナー兼コーダー出身のフロントエンド開発者です。 UXデザインやチーム開発の効率化など、勉強中です。
SHARE