ページ遷移しないWebシステムを作成する場合、いくつかの問題点に遭遇します。今回はその一つである、ブラウザの戻るボタン問題について解決していきます。
####以前の内容
[ページ遷移しないWebシステム] JavaScriptによるWindow Framework
https://qiita.com/SoraKumo/items/18197b5b61cf8061ae5d
[ページ遷移しないWebシステム]Qiitaの投稿をページ遷移無しで表示してみる
https://qiita.com/SoraKumo/items/0e017195d4ad8a88d762
####関連リンク
サンプルプログラム https://mofon001.github.io/npage/
サンプルソース https://github.com/mofon001/npage
ページ遷移しないBlog https://croud.jp/
戻れない戻るボタン
たとえば、JavaScriptでページ遷移しないブログシステムがあったとします。Ajaxによってコンテンツを非同期で拾ってきます。ユーザは操作を行い、いくつかの記事を切り替えました。そして、ふと、一つ前の記事が読みたくなり、ブラウザの戻るボタンを押します。
###「うわぁぁ、別のサイトに切り替わったぁぁぁ!!!!」
そうです、JavaScriptで画面を書き換えたページは、ブラウザの履歴には残らないのです。そして戻るボタンによって、一つ前に見ていたサイトに切り替わるのです。
オーイ、ミズシマ、イッショニ、マエノキジヘカエロウ
このネタが分かる人は少なそうです。それはどうでもいいとして、解決方法を考えます。実はJavaScriptには、履歴を操る方法が存在しているのです。
重要なのは以下の三つです
- location.search
- history.pushState
- popstateイベント
これらを利用して、理想的な戻るボタンの動きを実現します。
#ページの構築手順
以下の手順に従えば、だいたいうまくいきます。
- location.searchからページに関するパラメータを抽出
- パラメータに従い画面を書き換える
- ページ切り替えの操作が行われたら、history.pushStateで履歴をねつ造する
- ブラウザの戻るや進むボタンが押された場合、popstateが呼び出されるのでlocation.searchのパラメータに従い画面を書き換える
popstateで画面の書き換えを行う場合は、history.pushStateは呼び出してはいけません。既に履歴に入っているからです。
#サンプルプログラム
御託は良いから、とっとと出すもの出せやといわれそうなので、出しますサンプル
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<link rel="stylesheet" href="Main.css"/>
<script type="text/javascript" src="Main.js"></script>
<title>ページ遷移しないための履歴制御</title>
</head>
<body>
</body>
</html>
div:nth-child(1){
background-color: #cccccc;
padding: 10px;
}
div:nth-child(2){
margin:10px 0px;
background-color: #88ff88;
padding: 10px;
}
div:nth-child(3){
border: 1px solid;
height: 300px;
padding: 20px;
}
(function(){
//最初に実行するファンクションを設定
document.addEventListener("DOMContentLoaded",onLoad);
//ページが読み込まれた際に最初に呼び出される
function onLoad(){
//--------------------------------------------
//ページタイトルと経過時間表示場所の作成
var header = document.createElement("div");
document.body.appendChild(header);
header.innerHTML = "<span>ページ遷移しないプログラム 経過時間:</span><span>0</span>";
//ページが遷移していないことを確認するため、経過時間を表示
var t = header.querySelector("span:nth-child(2)");
setInterval(function(){t.textContent=parseInt(t.textContent)+1},1000);
//--------------------------------------------
//ページ切り替えボタンの作成
var menu = document.createElement("div");
document.body.appendChild(menu);
menu.innerHTML = "<button>ページ0</button><button>ページ1</button><button>ページ2</button>";
var buttons = menu.querySelectorAll("button");
buttons.forEach(function(v,i){v.addEventListener("click",function(){changePage(i,true)})})
//--------------------------------------------
//メインメッセージ表示領域の作成
var main = document.createElement("div");
document.body.appendChild(main);
//--------------------------------------------
//URLのパラメータ部分から、表示ページを切り替え
function goLocation(){
//パラメータの読み出し
var p = {};
location.search.substring(1).split('&').forEach(function(v){s=v.split('=');p[s[0]]=s[1];});
//指定ページに飛ぶ
changePage(p['p']>=0?p['p']:0,false);
}
//--------------------------------------------
//ページ切り替えとブラウザの履歴管理
function changePage(page,flag){
//flagがtrueなら、ページの状態を履歴に保存
if(flag)
history.pushState(null,null,"?p="+page);
//ページ内容を書き換え
main.innerHTML = ["あいうえお","かきくけこ","さしすせそ"][page];
}
//ブラウザの「戻る」「進む」ボタンが押された場合のイベント処理
addEventListener('popstate', function(){goLocation();}, false);
//初期ページへ飛ぶ
goLocation();
}
})();
私がWeb系のプログラムを作る際は、ちょっとした例外を除けばBODYタグの中は空です。ページ遷移しないプログラムは、基本的にベタでHTMLを書きません。JavaScriptで生成するのが基本なのです。HTMLを書くのは一番最初にjsファイルを読み出す部分と、innerHTMLなどに突っ込む部分だけです。
それから今回、DOMのノードを探す際、セレクターにclassやidを指定していません。使ってはいけない訳ではありませんが、それらに頼らないで目的のものが指定できるぐらいには、セレクターの指定方法は覚えておいた方が後々のためです。
ボタンを押すと内容が書き換わり、ブラウザのURLが変化します。そしてページ遷移が起こっていないことを確認するため、経過時間を表示しています。ボタンを何回か押した後、ブラウザの戻るボタンや進むボタンを押してみてください。ページ遷移せずに、必要な場所だけ書き換わるはずです。
#SEO対策
実は今回の内容は、ブラウザの戻るボタンだけの問題ではありません。SEO対策でも必要になるのです。サーチエンジンはURLが同一だと、違う内容が表示されていたとしても、別々のデータとしてキャッシュしてくれません。当たり前といえば当たり前です。今回のようにパラメータに意味を持たせれば、違うページとしてキャッシュしてくれるのです。
また、Googleで確認した結果、JavaScriptで動的に作成したページでも、きちんと内容を読み取ってキャッシュされました。ただしURLにパラメータを与えるのは必須条件です。
#まとめ
ページ遷移しないWebシステムは、ページのルーティングがサーバサイドにある限り実現できません。有名どころのフレームワークは壊滅です。
ページ遷移しないフレームワークを作るならば、クライアントサイドでルーティングさせる必要があります。その部分を作るのは、それほど難しいことではありません。結局の所、コントローラに相当する機能をクライアントに持っていくことになるので、距離ができてしまうサーバサイドとのデータのやりとりを、いかに簡潔に記述できるかが重要になります。
そんなこんなで何年か後には、ページ遷移しないことを前提としたフレームワークが当たり前の世の中になっていて欲しいところです。