Last Updated: February 25, 2016
·
2.222K
· johnnypez

No fuss active link states in Meteor

I had originally tried some methods where you register a global handlebars helper to set active states on links (this one mainly) but I found that to be a bit repetitive.

So what I do now is hook into the render callback of the app layout template.

I'm using Iron Router for routing and it is set to use a simple layout.

Whenever that layout is rendered I, look for any links that are active and remove that class, then where the href attribute matches the current path, I add an active class.

For my nav (using a bootstrap navbar) I do something similar but I only want to match against the stem of the url so that 'Posts' is still active when we're looking at '/posts/hello-world' etc.

Template.layout.rendered = ->
  setActiveLinks()

setActiveLinks = _.debounce ->
  path = location.pathname
  $("a[href].active").removeClass('active')
  $("a[href='#{path}']").addClass('active')
  # deal with top level nav
  stem = path.split('/')[1]
  $('.navbar-nav > li.active').removeClass('active')
  $(".navbar-nav a[href^='/#{stem}']").parent().addClass('active')
,250, true

I had first tried putting this feature in a global after filter for Iron Router but I found that sometimes my links would not be activated. This turned out to be a result of my nav tempalte being re-rendered by some other reactive elements.

The render callback for the layout template is triggered every time anything in the page changes so it made sense to put the link state handler there instead.

Lastly, the render callback on the layout template was being call a lot! So i used _.debounce to calm things down a bit. A wait of 250ms works for me but you might need to tune it.

2 Responses
Add your response

This no longer works in Blaze layout because render isn't always called.

Instead, in index.js I've done this:

Template.layout.rendered = function() {
Template._setActiveLinks();
};

Template.setActiveLinks = function() {
var path, stem;
path = location.pathname;
if(location.pathname !== '/') {
$("a[href].active").removeClass('active');
$("a[href='" + path + "']").addClass('active');
stem = path.split('/')[1];
$('.navbar-nav > li.active').removeClass('active');
return $(".navbar-nav a[href^='/" + stem + "']").parent().addClass('active');
}
}
And then I call it from:
onBeforeAction: function(){
Template.
setActiveLinks();
}

For every route, not ideal I'm sure...

over 1 year ago ·

Not sure why formatting is wanged on that comment...

over 1 year ago ·