Edited at

[ページ遷移しないWebシステム] JavaScriptによるURLの操作と、ページ遷移しないプログラム

More than 1 year has passed since last update.

 ページ遷移しない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イベント

 これらを利用して、理想的な戻るボタンの動きを実現します。


ページの構築手順

 以下の手順に従えば、だいたいうまくいきます。


  1. location.searchからページに関するパラメータを抽出

  2. パラメータに従い画面を書き換える

  3. ページ切り替えの操作が行われたら、history.pushStateで履歴をねつ造する

  4. ブラウザの戻るや進むボタンが押された場合、popstateが呼び出されるのでlocation.searchのパラメータに従い画面を書き換える

 popstateで画面の書き換えを行う場合は、history.pushStateは呼び出してはいけません。既に履歴に入っているからです。


サンプルプログラム

 御託は良いから、とっとと出すもの出せやといわれそうなので、出しますサンプル


index.html

<!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>


Main.css

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;
}



Main.js

(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を指定していません。使ってはいけない訳ではありませんが、それらに頼らないで目的のものが指定できるぐらいには、セレクターの指定方法は覚えておいた方が後々のためです。


実行画面

image.png

 ボタンを押すと内容が書き換わり、ブラウザのURLが変化します。そしてページ遷移が起こっていないことを確認するため、経過時間を表示しています。ボタンを何回か押した後、ブラウザの戻るボタンや進むボタンを押してみてください。ページ遷移せずに、必要な場所だけ書き換わるはずです。


SEO対策

 実は今回の内容は、ブラウザの戻るボタンだけの問題ではありません。SEO対策でも必要になるのです。サーチエンジンはURLが同一だと、違う内容が表示されていたとしても、別々のデータとしてキャッシュしてくれません。当たり前といえば当たり前です。今回のようにパラメータに意味を持たせれば、違うページとしてキャッシュしてくれるのです。

 また、Googleで確認した結果、JavaScriptで動的に作成したページでも、きちんと内容を読み取ってキャッシュされました。ただしURLにパラメータを与えるのは必須条件です。


まとめ

 ページ遷移しないWebシステムは、ページのルーティングがサーバサイドにある限り実現できません。有名どころのフレームワークは壊滅です。

 ページ遷移しないフレームワークを作るならば、クライアントサイドでルーティングさせる必要があります。その部分を作るのは、それほど難しいことではありません。結局の所、コントローラに相当する機能をクライアントに持っていくことになるので、距離ができてしまうサーバサイドとのデータのやりとりを、いかに簡潔に記述できるかが重要になります。

 そんなこんなで何年か後には、ページ遷移しないことを前提としたフレームワークが当たり前の世の中になっていて欲しいところです。