Backbone.js × jQuery pjax
はじめに
jquery-pjaxを使っているサイトのメンテナンス性あげるために、Backbone.jsでも入れようかなあと思ったところ、両者のpushState()が被って挙動がおかしくなった。
jquery-pjax
jquery-pjaxはjQueryのプラグインで、pushState×ajaxを実装したもの。
他にも、jquery-pjaxライクなプラグインはあるけど、やっぱりこれが一番使いやすい。
(function($) { $(document).pjax('a', '#container'); $(document).on('pjax:success', function() { console.log('pjax success!'); }); })(jQuery);
Backbone.js導入前はこんな感じに使っていた。
Backbone.js
簡単に言うと、javascriptのMVCフレームワーク。
jQuery($)とunderscore(_)に依存している。
にあるように、Backbone.Routerを使えばBackbone単体でpjaxは実装できる。
$(function() { var Router = Backbone.Router.extend({ routes: { "": "index", "hoge": "hoge" }, initialize: function() { this.container = $("#container"); }, index: function() { this.container.load("/"); }, hoge: function() { this.contaier.load("/hoge"); } }); window.router = new Router(); Backbone.history.start({pushState: true}); $("a").click(function() { window.router.navigate(location.pathname.substr(), { trigger: true }); }); })();
こんな感じ。
で、実装できるのは良いんだけど、
にも書かれているとおり、jquery-pjaxの至れり尽くせりな感じを捨ててBackboneにごにょごにょ書くのは面倒くさい。pjaxはjquery-pjaxで動かしつつ、Backbone.Routerも使いたい。
問題点
単純に、上のやつを書くだけだと、Backbone.Router.navigateとjquery-pjaxで二重にpushStateされてしまって2回戻らないと前のページに戻れない。
pop時にhistory.go()とかで2個戻るとか、replace: trueとかの組み合わせでなんとかならないかなと思ったけど、それも挙動が怪しくなる。
解決
じゃあもう、navigateでhistoryいじってるところ全部ナシにすればいいんじゃね?という考えに至り、
$(function() { var Pjax = Backbone.View.extend({ el: document, events: { "pjax:complete": "complete" }, initialize: function() { _.bindAll(this); this.$el.pjax("a", "#container"); }, navigateWithPjax: function(fragment, options) { // Backbone.history.navigate で必要な部分を抜き出す if (!options || options === true) options = {trigger: options}; var that = Backbone.history; fragment = that.getFragment(fragment || ''); if (that.history.fragment === fragment) return; that.history.fragment = fragment; if (options.trigger) that.loadUrl(fragment); }, conplete: function() { this.navigateWithPjax(location.pathname.substr(1), { trigger: true }); } }); var Router = Backbone.Router.extend({ routes: { "": "index", "hoge": "hoge" }, initialize: function() { this.container = $("#container"); }, index: function() { this.container.load("/"); }, hoge: function() { this.contaier.load("/hoge"); } }); window.router = new Router(); Backbone.history.start({pushState: true}); var pjax = new Pjax(); pjax.complete(); })();
やりかたとしてイケてるかは別にしてRouterの機能を使いつつjquery-pjaxで遷移するという当初の予定は達成出来たのでよしとする。
おわり
何か間違いや、より良い方法があれば教えてください。