Android4系の標準ブラウザではbackbone.jsのRouterが無限ループする件
起こったバグ
Android4の標準ブラウザにて、構築したサイトを開いたら、なんとリダイレクトの無限ループ。
設定内容
backbone.jsのrouterを使用して、開かれたURLによって、実行するjsを切り替えていた。
原因
Android2系までは対応していたのに、3系以降対応しなくなったらしい。
http://d.hatena.ne.jp/zentoo/touch/20111130/1322667152
ちなみにこのサイト(http://fmbip.com/)で対応しているかどうかチェックできる。
で、backboneではpushStateに対応していないと、URLのパス部分をfragmentとして、
hashタグに変換して、リダイレクトする仕様になっているが、
そのあたりでどうも無限ループに陥っていた。
対処
Backbone.Historyの引数とかnavigateでなんとか設定できないか探してみたが、わからなかったので、外部からstartの関数を書き換えることにした。
今回の制約事項として、スマホのみ。なのでIEの行は不要
// Start the hash change handling, returning `true` if the current URL matches // an existing route, and `false` otherwise. Backbone.History.prototype.start = function(options) { if (Backbone.History.started) throw new Error("Backbone.history has already been started"); Backbone.History.started = true; // Figure out the initial configuration. Do we need an iframe? // Is pushState desired ... is it available? this.options = _.extend({}, {root: '/'}, this.options, options); this._wantsHashChange = this.options.hashChange !== false; this._wantsPushState = !!this.options.pushState; // 20120603 mod for Android4.0 //this._hasPushState = !!(this.options.pushState && window.history && window.history.pushState); this._hasPushState = !!(this.options.pushState && window.history); var fragment = this.getFragment(); var docMode = document.documentMode; //var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); //if (oldIE) { //this.iframe = $('
').hide().appendTo('body')[0].contentWindow; //this.navigate(fragment); //} // Depending on whether we're using pushState or hashes, and whether // 'onhashchange' is supported, determine how we check the URL state. if (this._hasPushState) { $(window).bind('popstate', this.checkUrl); } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) { $(window).bind('hashchange', this.checkUrl); } else if (this._wantsHashChange) { this._checkUrlInterval = setInterval(this.checkUrl, this.interval); } // Determine if we need to change the base url, for a pushState link // opened by a non-pushState browser. this.fragment = fragment; var loc = window.location; var atRoot = loc.pathname == this.options.root; // If we've started off with a route from a `pushState`-enabled browser, // but we're currently in a browser that doesn't support it... //if (this._wantsHashChange && this._wantsPushState && !this._hasPushState && !atRoot) { //this.fragment = this.getFragment(null, true); //window.location.replace(this.options.root + '#' + this.fragment); //// Return immediately as browser will do redirect to new url //return true; //// Or if we've started out with a hash-based route, but we're currently //// in a browser where it could be `pushState`-based instead... //} else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) { //this.fragment = this.getHash().replace(routeStripper, ''); //window.history.replaceState({}, document.title, loc.protocol + '//' + loc.host + this.options.root + this.fragment); //} if (!this.options.silent) { return this.loadUrl(); } }
その他
そもそも、pushStateが使えなくても動くような、別なプラグインを入れるべきかもしれない。