実際には「pjax(pushState + ajax)」というjQueryのプラグインを使っていますが、今回はjQueryとpushStateのみでSPA(シングルページアプリケーション)を実装します。
以前紹介したCSSフレームワーク「Ratchet」は、まさに今回の応用です。
JavaScriptライブラリを使わない理由
SPAと言えば、AngularやReactの他、個人的に今後使いたいと思っているVueなどのJavaScriptのライブラリがたくさんありますが、私のような面倒臭がりには大きな問題があります。
例えば、新しく覚えなければならないことが増えたり、フロントだけでなくサーバー側の処理やレスポンスを変えるなど、今まで作ったものを変更したりするのは真っ平ゴメンです!
今回紹介する方法を使えば、既存のWEBアプリに導入するだけで、SPAに変えることができます。
Ajaxでビューを返して抜き出す
以下の方法なら、いつものHTML、CSS、JavaScript(Ajax)のみで、サーバー側も今まで通り普通にビューを返すだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
var content_id = '.content';$(function ($) { 'use strict'; if (window.history && window.history.pushState) { // click link with pushState $(document).on('click', 'a:not([href^="#"], [class*="ignore"])', function(e) { e.preventDefault(); var url = $(this).attr('href'); var title = $(this).attr('title'); history.pushState({url: url, title: title}, title, url); getAction(url); }); // form submit with pushState $(document).on('submit', 'form:not([action^="#"], [class*="ignore"])', function(e) { e.preventDefault(); var url = $(this).attr('action'); var title = $(this).attr('title'); history.pushState({url: url, title: title}, title, url); postAction(url); }); // send get function getAction(url) { $.get(url, { push_state: 'true' }, function(data) { loadContent(data); }) .fail(function() { loadError(); }); } // send post function postAction(url) { $.post(url, $("form").serialize(), function(data) { loadContent(data); }) .fail(function() { loadError(); }); } // content function loadContent(data) { var title = $(data).find('title').html(); var content = $(data).find(content_id).html(); $('title').html(title); $(content_id).html(content); } // error function loadError() { location.href='/error' // or $(content_id).html('<h1>Error!</h1>'); } // history back with popState $(window).on('popstate', function(e) { getAction(location.pathname); }); } }); |
Ajax + pushState の解説
初期設定
まずは、コンテンツを差し替えたい場所の要素を設定します。
1 |
var content_id = ".content"; |
次に、historyまたはpushStateが使えるブラウザかどうか判定します。
もし使えなければ、普通のWEBサイトとして表示します。
1 2 3 |
if (window.history && window.history.pushState) { ... } |
画面遷移のイベント
次に、リンク(アンカー)をクリックした時の処理(GET)と、フォームをサブミットした時の処理(POST)を書きます。
1 2 3 4 5 6 7 8 |
// click link with pushState $(document).on('click', 'a:not([href^="#"], [class*="ignore"])', function(e) { ... } // form submit with pushState $(document).on('submit', 'form:not([action^="#"], [class*="ignore"])', function(e){ ... } |
このイベントは、クリックまたはサブミットした際に遷移先のURLが「#」から始まらない、もしくはclassに「ignore」が存在しない場合に発生します。
イベントが発生したら、そのままページ遷移させないように、
1 |
e.preventDefault(); |
でイベントを一旦止め、
1 2 3 |
var url = $(this).attr('[属性名]'); var title = $(this).attr('title'); history.pushState({url: url, title: title}, title, url); |
遷移先のURLやタイトルを取得し、ブラウザのURLを書き換えて履歴(history.pushState)に登録。
最後に、GETの場合は、
1 |
getAction(url); |
そして、POSTの場合は、
1 |
postAction(url); |
関数を呼び出します。
GET/POST処理
関数内では、Ajaxの$.get()や$.post()メソッドを呼び出しています。
応答が帰ってきたら、
1 |
loadContent(data); |
を呼び出し、エラーの場合は、
1 |
errorContent(data); |
を呼び出します。
コンテンツを読み込む
受っとったHTMLデータから、タイトルとコンテンツを取り出して、
1 2 |
var title = $(data).find('title').html(); var content = $(data).find(content_id).html(); |
JavaScriptでHTMLを差し替えます。
1 2 |
$('title').html(title); $(content_id).html(content); |
以上です。
ページを遷移する際にフェードイン・フェードアウトを使うともっとアプリらしく見えますね!
あと、アンカーに[target=”_blank”]があった場合も除外した方がいいかも。
まあ、参考サイトのように16行とはなりませんが、GETだけでなくPOSTにも対応した上、かなり軽量だと思います。
一応、GitHubに上げてますので、よかったらどうぞ。
コメント