Sensebahnのサイト内では、ページを遷移するときにページ全体の更新ではなくページの一部を書き換える方法をとっています。(2013/1/31のデザイン変更で廃止しました。)
そうすることで、ページ全体を読み込むよりも速く遷移でき、さらに、画面がブランクになる瞬間が発生しないので目のチラツキを抑えられます。
仕組みとしてはAjaxを使います。
AjaxはJavaScriptで簡単に使うことができます。
JavaScriptを使って、Ajaxによりページの一部を、他のページの内容に書き換えるのです。具体的には、次のコードでやります。
example.html
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> <script type="text/javascript" src="example.js"></script> <!-- 以下HTML -->
example.js
var changePage = function(){ $("読み込んだ内容を格納する要素").load("http://読み込みたいページのURL " + "読み込みたい要素",コールバック関数); //読み込みたいページのURLの後ろに半角スペースを入れること } $(document).on("click","a",changePage);
JavaScriptフレームワークであるjQueryを読み込んだら、あとはjQueryの.load()を使うだけです。この例では、すべてのa要素をclickしたときに、指定の要素内に、指定のページの指定の要素を読み込ませます。
しかし問題があります…
ページの一部を書き換えているだけなので、ブラウザのアドレスバーが変わらないのです。だから、履歴にも残りませんし、ブックマークもできませんし、Facebookでシェアすることもできません。(全部、はじめに開いたページになってしまいます)
解決するにはpushState
その問題を解決するのが、HTML5で導入されたpushStateというAPIです。
やっと本題です_| ̄|○
pushStateによって、Ajaxを使ってスムーズなページ遷移を実現させつつ、アドレスバーも動的に変えることができます。このpushStateをjQueryで簡単に使えるようにする、PjaxというjQueryプラグインがあります。しかし当サイトでは思い通りの挙動にならなかった(^_^;)ので、プラグインを使わずに素のJavaScriptAPIを呼び出しました。
次のコードでできます。
example.js
var after = function(){ history.pushState("","","ページの相対パス"); //アドレスバー変更 } var changePage = function(){ $("読み込んだ内容を格納する要素").load("http://読み込みたいページのURL " + "読み込みたい要素",after); //Ajaxで一部更新 } $(document).on("click","a",changePage);
history.pushState()で使えます(第1〜2引数は利用シーンがあまりないと思うので割愛しますね)。注意点としては、pushStateでアドレスバーを変更すると、現在のホストの後部に指定の文字列が追加されるため、相対パスでないといけないということです。(ホストまで変えることができたら悪用されちゃいますから、当然の仕様です)
それと当然ながら、ブラウザがpushStateに対応している必要があります。MDNが対応ブラウザをまとめているので、これが参考になると思います。ページ下部にあるBrowser compatibilityという項目です。
実践
このpushStateのほか、当サイトでは遷移時にアニメーション効果を付けています。より実践的には次のようなコードを使っています。
example.html
<!-- headなど前略 --> <link href="/css/example.css" rel="stylesheet" type="text/css" media="screen"> <!-- CSS読み込み --> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> <!-- jQuery読み込み --> <script type="text/javascript" src="/js/jquery.easing.js"></script> <!-- jquery.easing.js読み込み http://gsgd.co.uk/sandbox/jquery/easing/ --> <script type="text/javascript" src="/js/example.js"></script> <!-- bodyなど中略 --> <div id="nav"> <a href="http://〜">リンク</a> </div> <div id="ajax-wrapper"> <div id="ajax-content"> <div id="main"> <!-- コンテンツ --> </div> </div> </div> <div id="curtain"></div> <!-- footerなど後略 -->
example.js
var changePage = function(){ windowH = $(window).height(); //画面の高さを取得 request = $(this).attr("href"); //リンク先URLを取得 url = location.host; thisProt = location.protocol; relativePath = request.replace(thisProt,"").replace("//","").replace(url,""); //相対パスの作成 if(relativePath == ""){ relativePath = "/"; } //相対パスが空だったときはスラッシュを代入 after = function(){ $(".loader").remove(); //ローダーを削除 } refreshInfo = function(){ history.pushState("", "", relativePath); //pushStateでアドレスバーを変更 pageTitle = $("h1").text(); //新しいページタイトルとしてh1を取得 $("title").text(pageTitle); //ページタイトルを変更 after(); //afterを実行 } displayContent = function(){ $("html,body,#main").animate({scrollTop: 0},1000); //画面位置を上に戻す $("#curtain").animate({"height":0},800,"easeOutExpo",refreshInfo); //カーテンの高さを戻したらrefreshInfoを実行 } loadContent = function(){ $("#ajax-wrapper").load(request + " " + "#ajax-content",displayContent); //#ajax-wrapperにリンク先の#ajax-contentを読み込んだらdisplayContentを実行 } _gaq.push(["_trackPageview",request]); //GoogleAnalyticsのトラッキングページを変える $(this).append('<div class="loader"></div>'); //リンクの横にローダーを表示 $("#curtain").animate({"height":windowH},800,"easeOutExpo",loadContent); //カーテンを画面の高さぶん動かしたらloadContentを実行 return false; } $(document).on("click","#nav a",changePage); //#navの中のaがclickされたらchangePageを実行
example.css
a { position: relative; } .loader { position: absolute; width: 16px; height: 100%; left: -17px; bottom: 0; background-image: url("images/loader.gif"); background-position: center left; background-repeat: no-repeat; } #curtain { position: fixed; width: 100%; height: 0; background-color: white; background-image: url("images/stripe.png"); z-index: 1; top: 0; } /* widthは適宜変えてください */
実際には、このサイトの独自仕様などがあるのでもう少し複雑なことをしていますが、一般的な部分ですとこんな感じです。
いかがでしょう。参考になれば幸いです!
pushStateの記事参考になりました。
もしご存知なら教えて頂きたいのですが、pushStateの場合、OGPの書き換えも動的に可能なのでしょうか?
昨日発表になった、FBのグラフ検索等を考えるとOGPは非常に重要な要素になると思いますが、pushStateの場合、1ページに一つのOGPしか設定出来ないので、ページ単位で内容
を変更する事は可能なのでしょうか?
開発側の人間からするとpushStateは非常に有効な手法なので上記件解決すると非常に嬉しいのですが・・・
はじめまして!
ちょっと勘違いでしたらすみませんが…具体的にはFacebookの「いいねボタン」を動的に変えるという事でしょうか?それならここでも実装しているので、ちょうど近々記事にしようと思ってました。優先して書こうと思うので、しばしお待ちください!
それか、ここでは実装していませんが、<meta property=”og:audio” content=”mp3等のURL”>などのmetaタグを動的に変えたいという事でしょうか?
その場合は、jQueryで $(“meta[property=og:audio]”).attr(“content”,”新しいURL”); としてやれば書き換えられると思います。
的外れでしたらすみません・・・。
ご返答ありがとうございます。
metaの方ですね。ページで、FBに送る(Twitterも昨年から導入)情報を制御する為の情報になるので動的に書き換えたいのですが、JS側で書き換えても有効になりませんでした。
特に、og:title・og:description・og:image・og:urlはページ単位で書き換えたいという要望が多く困ってました。結局、ページを分けて対応しました・・・
JSで書き換えるタイミング等にもよるのかと思っていたのでご質問した次第です。
なるほど。たしかに、Chromeの「要素を検証」ツールを使って手で書き換えてみましたが、Facebookには反映されないですね。FacebookのDebuggerを使えばFacebook側のキャッシュをクリアできるので反映されそう(参考: http://blog.mania-buddy.com/2012_06_13/178 )ですが、ページ遷移の都度キャッシュを消すのもスマートじゃなさそうです(^_^;)
うちではWordPressを使っていて、metaの生成にはFacebook純正のプラグインを使用しているんですが、Ajaxでページ遷移してもmetaが更新されていますね…。
Chromeの「要素を検証」でネットワークを観察してみると、Ajaxでのページ遷移後にFacebookと3回ほど通信が発生していますが、いずれもlike.phpによるものなのでmetaとは関係無さそうです。
僕が更新してあげてるのは「いいねボタン」だけなので、metaは完全にWordPressのプラグインがやってくれてるようです。プラグインが裏でやってる作業を見ればヒントがあるはずなので、ちょっと調べてみたいと思います。
あっ、もしサイト構築にWordPressを使っているようでしたら、Facebookの純正プラグインをお試しください(^_^;)
テストサイトでちょっと試してみたんですが、OGPは「ブラウザで表示されているmetaは見ない」ようです。OGPはそのページをシェアしようとしたときに「FacebookがそのURLにアクセスしてmetaを取得する」ようです。(確証はないのですが挙動を見るにそうです…すでにご存知でしたでしょうか?(^_^;))
ブラウザ上でmetaを変えても、実際にFacebookが見てるのはサーバーサイドなので、何も反映されないということですね。なのでjsでmetaを変える必要はなくて、遷移先のURLに直接アクセスしたら正しいmetaを取得できるようになってればいいはず…。
ここではAjaxによって部分的にページ更新をしていますが、遷移先(pushStateで新しくしたアドレス)のURLに直接アクセスしてもそのページが見れるようになっておられますか?
FacebookはアドレスバーのURL(もしくはシェアボタンに設定したURL)に直接アクセスしてくるので、そのURLで表示されるHTMLに正しいmetaが設定されていれば良いのかと、推測しました。
[…] jQueryだけで(Pjaxを使わずに)Ajax+pushStateする方法という記事を書きましたが、じつはAjaxでページを切り替えると、いろんな不都合が生じることが多いです。 […]
どんな不都合でしょうか?
[…] このサイトでもAjax+pushStateなどの技術を使うためにjQueryを多用しています。 […]
[…] http://sensebahn.com/develop/273 http://blog.toshimaru.net/jquery-ajaxdeferredajax/ http://hamalog.tumblr.com/post/7978448438/ajax-abort […]
バック グッチ
あなたのテーマが魅力的だった。