I am migrating a single-page web application based on Backbone.js and jQuery to a Chrome extension. However, neither the pushState
nor the hashbang-based router modes seem to play well with the environment within the extension. I've come to the conclusion that I'm better off just directly rendering views on user interactions, bypassing the window.location
system altogether. However, I'm not too sure how to implement this without changing calls to Router.navigate
in dozens of files.
Is there a pluggable/modular way to keep the Backbone routing system but bypass any changes to the url?
I you really want to stick with using Router.navigate
to benefit from the routing system that Backbone.js provides without having to deal with hashbang bugs when used in a Chrome extension (e.g. routes including a slash being overwritten), you could make Router.navigate
load the url directly, skipping the whole pushState gymnastic.
This is actually pretty easy to accomplish:
Router = Backbone.Router.extend({
navigate: function (url) {
// Override pushstate and load url directly
Backbone.history.loadUrl(url);
},
// Put routes here
routes: { }
});
You can then call Router.navigate(url)
to load a new route without changing history, or even bind the action to every link containing a data-backbone
attribute (e.g. <a href="login" data-backbone>Login</a>
) with an event like this:
$(function(){
// Initialize router
Router = new Router;
Backbone.history.start();
// Bind a[data-backbone] to router
$(document).on('click', 'a[data-backbone]', function(e){
e.preventDefault();
Router.navigate( $(this).attr('href') );
});
});
You could redefine what Router.navigate
does, but you're better off just not using the Background.router altogether. I think it could create some confusion, and your code will be cleaner without it if you're currently triggering history changes from views.
Backbone.Marionette has a concept of Controllers, which work a lot like routers without a URL-map (with Marionette, the idea is to keep your route definitions minimal, and call controllers for the behavior instead). You also don't need to use any of the components of Marionette you don't want.
If you really wanted to stick with the router as it is, you could probably just redefine Backbone.History.navigate
to (note, untested)
navigate: function(fragment, options) {
if (!History.started) return false;
if (!options || options === true) options = {trigger: options};
fragment = this.getFragment(fragment || '');
if (this.fragment === fragment) return;
this.fragment = fragment;
if (options.trigger) this.loadUrl(fragment);
}