ナビ部分は固定されていてコンテンツ部分だけが切り替わっていく。かつurlもきちんと変更されている。
そんなサイトをご覧になったことはありますでしょうか。
今回は『pjax』を使ってそんなサイトを制作する方法をご紹介します。
具体的な要件は以下の通りです。
- 軽量なプラグイン『Barba.js』を使ってpjax対応をする
- jQueryを使ってページ遷移時にアニメーション(フェード)をつける
- ページ遷移時にurlが変更される
- ナビゲーション部分を固定してコンテンツ部分だけが変わっていく
まずは以下のリンクからデモをご覧ください。
jQueryとBarba.jsでpjax対応サイトを実装するdemo
ヘッダー(背景が白い部分)は固定されており、リンクをクリックするとコンテンツ部分(背景がグラデーションの部分)のみがフェードで切り替わる。
合わせてurlも変更されていることをご確認いただけましたでしょうか。
ここからはそもそも『pjax』とは? から始まり、実装方法、問題点と解決方法を紹介していきます。
CONTENTS
そもそも『pjax』とは
ひとことで言うと『pjax』とは、『pushState』と『ajax』の技術を組み合わせたjQeuryのプラグインのことを指します。
これだけだと何のことだかさっぱりわかりませんね。
もう少し具体的に説明していきます。
『pushState』とは
HTML5から追加されたHistory API(履歴操作をするためのツール)の機能のひとつで、ブラウザの履歴に対する操作をします。
これによってHTMLの一部のみを書き換える『ajax』でもブラウザの戻る/進む機能を実現できるようになりました。
『ajax』とは
カタカナ読みをすると『エイジャックス』。JavaScriptを利用しサーバとの間の通信(データのやり取り)を非同期で行うことができます。
Googleマップなどが有名ですね。地図をスクロールさせると必要な部分だけが次々と読み込まれていきます。
この必要な部分だけというのがポイントで使える情報はそのままに、不足している情報だけを取得。
ユーザーにもサーバーにも負荷の少ない仕組みです。
その仕組みを利用して、例えばヘッダーは固定でコンテンツ部分だけが入れ替わっていくサイトなどが登場してくるのですがひとつ問題がありました。
Ajaxでページ遷移するとURLは更新されず、ブラウザの『戻る』が使用できなかったんですね。
それを解決する方法が上記の『pushState』です。
『pjax』まとめ
先ほどのまでの説明を踏まえてまとめると『pjax』とは以下のような解釈(あくまでも個人的な解釈ですが)になります。
『ヘッダーやフッターなど変わらない部分はそのまま残しておいて、コンテンツ部分だけを入れ替える。非同期通信なのでユーザーやサーバーにも優しい。ページ遷移時にはurlもきちんと変更されるためSEO的にもバッチリ。そんなサイトを作る方法の一つ。』といったところでしょうか。
実装方法
では、ここからは実際の流れにそって『pjax』対応のdemoサイトを制作していきます。
今回は『Barba.js』というjQueryのプラグインを使用します。
『Barba.js』をダウンロード
簡単に紹介すると『Barba.js』とはページ遷移時に必要な部分だけを差し替えて高速化。かつアニメーションも付与できるプラグインです。
以下のリンク(github)より必要なファイルをダウンロードします。
詳細な説明や参考になるサイトも掲載されていますので一度読み込んでみることをおすすめします。
必要になのはdistフォルダ内にあるbarba.min.jsのみです。
ダウンロードしたら任意のフォルダにアップし、『Barba.js』を読み込む記述をします。
今回はページ遷移にアニメーションをつけるので一緒にjQueryも読み込んでおきます。
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="js/barba.min.js" type="text/javascript"></script>
HTMLの記述
demoサイトの記述を元に説明します。
barbajs-1.html(1ページ目)
<header>
<div class="header-inner">
<div class="logo">
<h1>Header</h1>
</div><!-- /.logo -->
<nav id="nav-area">
<ul>
<li><a href="https://oku-log.com/sample/barbajs-1.html">page-1</a></li>
<li><a href="https://oku-log.com/sample/barbajs-2.html">page-2</a></li>
<li><a href="https://oku-log.com/sample/barbajs-3.html">page-3</a></li>
</ul>
</nav><!--/.nav-area-->
</div><!-- /.header-inner -->
</header>
<div id="barba-wrapper" aria-live="polite">
<div class="barba-container">
<div class="inner page-1">
<div class="content">
<h2>This is page-1</h2>
<ul>
<li><a href="https://oku-log.com/sample/barbajs-2.html">page-2</a></li>
<li><a href="https://oku-log.com/sample/barbajs-3.html">page-3</a></li>
</ul>
</div><!-- / .content -->
</div><!-- / .inner -->
</div><!-- / .barba-container -->
</div><!-- / #barba-wrapper -->
重要なのは以下の部分です。
barba-wrapperおよびbarba-containerで囲まれた部分だけがページ遷移時に差し代わります。
demoの例では固定のheader部分はbarba-wrapperの外側においているのでコンテンツ部分だけが差し代わるようになっています。
<div id="barba-wrapper" aria-live="polite">
<div class="barba-container">
〜ページ遷移時に差し代わる部分〜
</div><!-- / .barba-container -->
</div><!-- / #barba-wrapper -->
続いてjsの記述
アニメーションなどを実行させるとなるとコードが長くなります。
管理のしやすさを考慮して別ファイルで読み込ませた方があとあと楽です。
今回の例では『barba-custom.js』というファイルを作成して以下のようにhtmlファイルに読み込みました。
<script src="js/barba-custom.js" type="text/javascript"></script>
ここからはjsの記述をしていきます。
まずは最低限の記述から。
barba-custom.js(ファイル名は任意)
//Barba.jsを動かすのに最低限必要な記述。
Barba.Pjax.start();
たったこれだけ。
この1行を記述するだけでアニメーションはありませんが最低限の動きはできるようになります。
続いてアニメーションの設定をします。
barba-custom.js(先ほど記載したBarba.Pjax.start();よりも前に記述)
//Barba.js用トランジション設定
var FadeTransition = Barba.BaseTransition.extend({
start: function() {
//この関数は、遷移が開始されるとすぐに自動的に呼び出されます。
//読み込みが終了し、古いページがフェードアウトするとすぐに、新しいページがフェードインします。
Promise
.all([this.newContainerLoading, this.fadeOut()])
.then(this.fadeIn.bind(this));
},
fadeOut: function() {
//this.oldContainerは古いコンテナのHTMLElementです。この場合はアニメーションでフェードアウトさせている。
return $(this.oldContainer).animate({ opacity: 0 }).promise();
},
fadeIn: function() {
var _this = this;
//this.newContainerは、新しいコンテナのHTMLElementです。
//この段階では、newContainerはDOM上にあります(barba-container内にあり、visibility:hiddenで囲まれています)。
var $el = $(this.newContainer);
//古いコンテナをdisplay:none;にした後、新しいコンテナに初期値を設定。
$(this.oldContainer).hide();
$el.css({
visibility : 'visible',
opacity : 0
});
$el.animate({ opacity: 1 }, 400, function() {
//.done()の記述で古いコンテナを自動的にDOMから削除。
_this.done();
});
}
});
// returnに作ったトランジションを設定。
Barba.Pjax.getTransition = function() {
return FadeTransition;
};
説明を入れつつ記述をしていますが、簡単にいうと古いページの内容をフェードアウトさせて、新しいページの内容をフェードインさせています。
だいぶそれっぽくなってきましたね。
参考までにCSSを紹介しておきます。
demo用にざっくり作ってあるので参考までに。
body {
font-family: 'Josefin Sans', sans-serif;
background: #fff;
height: 100vh;
overflow: hidden;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: none;
color: #e3be08!important;
}
header {
background: #fff;
overflow: auto;
top: 0px;
left: 0px;
width: 100%;
padding: 35px;
text-align: center;
}
header h1 {
margin-bottom: 30px;
font-size: 40px;
font-weight: bold;
display: block;
line-height: 1;
letter-spacing: 0;
padding: 0 20px;
}
nav li {
display: inline-block;
}
nav li a {
font-size: 18px;
padding: 20px;
}
nav li:first-child a:hover {
color: #70e4d0!important;
}
nav li:nth-child(2) a:hover {
color: #e0b15c!important;
}
nav li:nth-child(3) a:hover {
color: #f3bbc3!important;
}
#barba-wrapper {
padding: 0;
width: 100%;
}
.inner {
display: table;
width: 100%;
}
.inner .content{
display: table-cell;
vertical-align: middle;
text-align: center;
}
.inner h2{
font-size: 30px;
margin-bottom: 50px;
margin-top: 0;
}
.inner ul li{
display: inline-block;
padding: 10px 20px;
}
.inner ul li a{
background: rgba(79,77,71,.1);
border-radius: 5px;
padding: 18px 50px;
color: #000;
font-size: 18px;
display: block;
line-height: 1;
}
.inner ul li a:hover{
color: #000!important;
background: rgba(79,77,71,.05);
}
.page-1.inner {
background: -webkit-gradient(linear, left bottom, right top, color-stop(1%, #7be7d7), color-stop(40%, #7be7d7), color-stop(40%, #7be7d7), color-stop(100%, #86a4e3));
background: -webkit-linear-gradient(45deg, #7be7d7 1%, #7be7d7 40%, #7be7d7 40%, #86a4e3 100%);
background: -webkit-linear-gradient(45deg, #7be7d7 1%, #7be7d7 40%, #7be7d7 40%, #86a4e3 100%);
background: linear-gradient(45deg, #7be7d7 1%, #7be7d7 40%, #7be7d7 40%, #86a4e3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#7be7d7', endColorstr='#86a4e3', GradientType=1);
}
.page-2.inner{
background: -webkit-gradient(linear,left bottom,right top,color-stop(0%,#ddb14f),color-stop(100%,#ea6c30));
background: -webkit-linear-gradient(45deg,#ddb14f 0,#ea6c30 100%);
background: -webkit-linear-gradient(45deg, #ddb14f 0, #ea6c30 100%);
background: linear-gradient(45deg,#ddb14f 0,#ea6c30 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ddb14f', endColorstr='#ea6c30', GradientType=1);
}
.page-3.inner{
background: -webkit-gradient(linear,left bottom,right top,color-stop(0%,#f2c8c5),color-stop(100%,#f686bc));
background: -webkit-linear-gradient(45deg,#f2c8c5 0,#f686bc 100%);
background: -webkit-linear-gradient(45deg, #f2c8c5 0, #f686bc 100%);
background: linear-gradient(45deg,#f2c8c5 0,#f686bc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f2c8c5', endColorstr='#f686bc', GradientType=1);
}
Barba.jsを使うにあたっての問題点と解決方法
『Barba.js』は軽いし、使い勝手もいい方なのですが実際にサイト制作をするとなるともう少し記述を追加していく必要があります。
問題点1:コンテンツ部分の内容やタイトルは差し代わるけどメタ情報が更新されない
SEO的にはこの問題はかなり痛いですね。でも安心してください。ちゃんと解決策はあります。
『Barba.js』で用意されているイベントを以下のように追記して、メタ情報も更新されるようにします。
barba-custom.js
Barba.Dispatcher.on('newPageReady', function( currentStatus, oldStatus, container, newPageRawHTML ) {
//head内のタグを更新
if ( Barba.HistoryManager.history.length === 1 ) { // ファーストビュー
return;
}
var $newPageHead = $( '<head />' ).html(
$.parseHTML(
newPageRawHTML.match( /<head[^>]*>([\s\S.]*)<\/head>/i )[ 0 ],
document,
true
)
);
var headTags = [ // 更新が必要なタグ
"meta[name='keywords']",
"meta[name='description']",
"meta[property^='og']",
"meta[name^='twitter']",
"meta[itemprop]",
].join( ',' );
$( 'head' ).find( headTags ).remove(); // タグを削除
$newPageHead.find( headTags ).appendTo( 'head' ); // タグを追加
return false;
});
これでページ遷移した際にメタ情報も合わせて更新されるようになりました。
問題点2:読み込んでいたスクリプトがページ遷移すると効かなくなる
例えばdemoサイトでは画面全体の高さからヘッダーの高さを引いた数値をコンテンツ部分の高さにするために以下のようなスクリプトを記述しています。
jQuery(function($){
var winHeight = $(window).height();
var headerHeight = $('header').innerHeight();
var innerHeight = winHeight - headerHeight ;
$('.inner').css("height", innerHeight + "px");
});
最初に開いたページではこの記述がちゃんと読まれるのですが、ページ遷移のタイミングでこのスクリプトを読み込む記述がないのでページ遷移をするとスクリプトが効きません。demoでいうと高さが取得出来ずコンテンツ部分の下に余白が出来てしまいます。
そこで先ほどのメタ更新と同じようにページ遷移時にスクリプトを読む記述を追記します。
barba-custom.js
Barba.Dispatcher.on('newPageReady', function( currentStatus, oldStatus, container, newPageRawHTML ) {
//ページ読み込み時にスクリプトを再読み込み
var winHeight = $(window).height();
var headerHeight = $('header').innerHeight();
var innerHeight = winHeight - headerHeight ;
$('.inner').css("height", innerHeight + "px");
return false;
};
これでページ遷移してもスクリプトが反映されるようになりました。
まとめ
今回はdemoサイトを使って『pjax』を実装する方法を紹介しました。
『pjax』にも、使用したプラグイン『Barba.js』にもまだまだ出来ることはあって奥は深いですが、まずは少しずつ試してみてはいかがでしょうか。