Search code examples
navigationbackbone.jshighlighting

Highlighting current navigation state in Backbone.js application


I want to highlight the current navigation state. Like if the hashchange is #home, I want to style the 'Home' menu link differently and similarly other links.

Backbone.js fires individual events like route:home,... route:some-other when the #home and other links are clicked. I could not see any common event that will be fired for every hashchange. With this I m required to write the state highlight logic by binding to all the route events, which I think is not good solution.

So, I've overridden Backbone.Router.route in my router sub class/object, like

// override backbone' Router.route method to publish 
// common 'route_change' event for any hash change
route : function(route, name, callback) {
    Backbone.history || (Backbone.history = new Backbone.History);
    if (!_.isRegExp(route)) route = this._routeToRegExp(route);
    Backbone.history.route(route, _.bind(function(fragment) {
        var args = this._extractParameters(route, fragment);
        callback.apply(this, args);
        this.trigger.apply(this, ['route:' + name].concat(args));

        // ADDED BY: ManiKanta G
        // name: route method
        // fragment: route path
        // args: any additional args
        this.trigger.apply(this, ['route_change'].concat(name, fragment, args));
    }, this));
}

This will publish a common route_change event for every hashchange and passing the name, fragment, and other args using which I m highlighting the state all in a single place.

My question is do I have to override the Backbone method like this or is there any build in mechanism I can use here. If not, I would like to see similar behaviour in Backbone.js

Edit: sample program

Router = Backbone.Router.extend({
    routes : {
        '': 'root', 
        'home': 'home',
        'about':'about'
    },

    // app routing methods
    root: function () { console.log('root route');  },
    home: function () { console.log('home route');  },
    about: function () { console.log('about route'); }

});

Router.bind('all', function () {
    console.log('all route...');
});

router = new Router();

and, navigating using the above router:

router.navigate('home', true);

output: home route

Update on why the above program is not working:

we should bind for all event on Router instance, but not on the Router itself - so, changing the Router.bind('all', ... to router.bind('all', ...) will make the above program work


Solution

  • In backbone 0.5.x you can bind all event to router instance and the first argument pass to your handler will be route

    Here is exemple on jsfiddle, reproduced here:

    var dummy = Backbone.Router.extend({
        defaultPage: 'messages',
    
        routes: {
            '': 'index',
            'index': 'index',
            'mailbox': 'mailbox'
        },
    
        index: function() {
            // code here
        },
    
        mailbox: function() {
            // code here
        }
    });
    
    var router = new dummy();
    
    router.bind('all', function(route) {
        document.write('triggered: ' + route + '<br/>');
    });
    
    router.navigate('index', true);
    router.navigate('mailbox', true);