どうも、フロントエンドエンジニアのスギヤマです。
前回の記事で脱jQuery!?な記事を書きましたが、その流れで〇〇使ってみた、作ってみた、みたいなものを書いていきたいと思います。
今回はPJAXライブラリ-Barba.js-。PJAXはjQueryプラグインでも実装でき使ったこともありますので軽く比較などしてみたいと思います。
そもそもPJAXはAjaxとpushStateを組み合わせて非同期通信でページの切り替えを行うもので、指定したエリアのみ通信が発生するのでスムーズなページ遷移が可能になります。
では、jQueryプラグインからいってみましょう。
jQueryプラグイン jquery-pjaxにはfalsandtru/jquery-pjaxとdefunkt/jquery-pjaxの2種類があるようですがこの先はdefunkt版です。
jQuery本体と、jquery.pjax.jsをダウンロードします。
[before.html]
<div class="btn"> <a class="is-pjax" href="after.html">after</a> </div> <div id="before-contents"> <!--↓ここが塗り変わる↓--> <p>変わる前</p> <!--↑ここが塗り変わる↑--> </div>
[after.html]
<div id="after-contents"> <!--↓ここに塗り変える↓--> <p>変わる後</p> <!--↑ここに塗り変える↑--> </div>
[pjax.js]
$(document).on('click','.is-pjax',(e)=>{ e.preventDefault(); const _self:HTMLElement = e.target; const href = $(_self).attr('href'); $.pjax({ container : '#before-contents', //前のコンテンツ fragment: '#after-contents', //後のコンテンツ url : href }); });
DOMを書き換える際イベントが消えてしまうため、
直接セレクターにではなく、$(document)にイベントをバインドさせています。
PJAXを実行するボタンなどのclass名は自由に設定できます。
$(document).on('pjax:beforeSend', function() { console.log('ajaxリクエストが開始される前に発生'); }); $(document).on('pjax:popstate', function() { console.log('ブラウザの戻る進むなどを行った時に発生'); }); $(document).on('pjax:start', function() { console.log('pjaxが開始されたときに発生'); }); $(document).on('pjax:send', function() { console.log('ajaxリクエストを開始した後に発生'); }); $(document).on('pjax:success', function() { console.log('ajaxリクエストが成功した後に発生'); }); $(document).on('pjax:error', function() { console.log('ajaxリクエストが失敗した後に発生'); }); $(document).on('pjax:timeout', function() { console.log('タイムアウト時に発生'); }); $(document).on('pjax:complete', function() { console.log('ajaxリクエストが終了した後に発生'); }); $(document).on('pjax:end', function() { console.log('pjaxが終了したときに発生'); });
pjax:popstateなどイベントが用意されていて、開始される前、終了時にアニメーションなど入れておけばスムーズに遷移しているように見えます。
PJAXするのにpushStateなどが必要になってくるのでサポートブラウザはそれらが使えるモダンブラウザ&IE10〜。 ただ公式リファレンスでも使用していますがPromiseを使用しているのでIE11でもPolyfillが必要になります。
<script src="https://www.promisejs.org/polyfills/promise-6.1.0.min.js"></script>
ほんとにIEってやつは。。
barba.js本体をダウンロードします。
[before.html]
<div class="btn"> <a href="after.html">after</a> </div> <div id="barba-wrapper"> <div class="barba-container"> <!--↓ここが塗り変わる↓--> <p>変わる前</p> <!--↑ここが塗り変わる↑--> </div> </div>
[after.html]
<div id="barba-wrapper"> <div class="barba-container"> <!--↓ここに塗り変える↓--> <p>変わる後</p> <!--↑ここに塗り変える↑--> </div> </div>
[pjax.js]
let PageTransition = Barba.BaseTransition.extend({ start: function() { const oldNode = this.oldContainer; const pageOut = new Promise(function(resolve, reject) { // 遷移前の処理など resolve(); }); Promise .all([this.newContainerLoading, pageOut]) .then(this.pageIn.bind(this)); }, pageIn: function() { const self = this; const $el = this.newContainer; // 遷移後の処理など } }); Barba.Pjax.getTransition = function() { return PageTransition; }; Barba.Pjax.start();
jquery-pjaxと違って入れ替える要素のIDとClass#barba-wrapper,.barba-containerは固定になります。
PJAXを実行するボタンにclasなどの指定はなく.barba-container内すべての<a>が対象になります。
あれこれ書いていますがBarba.Pjax.start();さえあればPJAXは可能になります。
Barba.Dispatcher.on('newPageReady', function(currentStatus, oldStatus, container, newPageRawHTML) { const head = document.head; const newPageRawHead = newPageRawHTML.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0]; const newPageHead = document.createElement('head'); newPageHead.innerHTML = newPageRawHead; const removeHeadTags = [ "meta[name='keywords']" ,"meta[name='description']" ,"meta[property^='og']" ,"meta[name^='twitter']" ,"meta[itemprop]" ,"link[itemprop]" ,"link[rel='prev']" ,"link[rel='next']" ,"link[rel='canonical']" ].join(','); const headTags = head.querySelectorAll(removeHeadTags) for(let i = 0; i < headTags.length; i++ ){ head.removeChild(headTags[i]); } const newHeadTags = newPageHead.querySelectorAll(removeHeadTags) for(let i = 0; i < newHeadTags.length; i++ ){ head.appendChild(newHeadTags[i]); } });
※正規表現部分の<,>は大文字化しています。文字化けしてしまうので。。
jquery-pjaxもですがBarba.jsではtitleタグの変更を行ってくれますがそれ以外のメタデータを変更してくれません。
ですのでDispatcher部分でnewPageReady(ページ遷移開始)にhead内の要素を取得し変更する処理を書いています。
こちらを参考にさせていただきました。。というかママ笑
公式のリファレンスではなぜかjQuery使用しているので使わないように書き換えていますが、上記pageOutをpageInと並列にしようとすると何故かPromiseでちゃんと処理終了を待ってくれないのでstart内にpageOutの処理を書いています。
ちなみに公式のjQuery使ったものでもちゃんとPromiseしてくれていなさそう。。(小声)
ページ毎に処理を変えたい場合は.barba-containerにdata-namespaceを指定します。
<div class="barba-container" data-namespace="homepage">
双方使ってみてBarba.jsの方はhead周りのものもあるのでコードが多くなっていますが、それを除けばjquery-pjaxまではいかないもののかなりお手軽に組み込めるなと言った所感です。
遷移時のアニメーションはjQueryの方はanimate()を使えば良いですが、Barba.jsの方は別途anime.jsなど使ってあげればアニメーションもお手軽に実装できるかと思います。