Nav Highlighting with Ember and Bootstrap

By Zach Foster on August 01, 2016

ember bootstrap nav highlighting active

compass Image by phrawr

Giving users access to information about the state of your application is one of the most important facets of developing a good front end. It’s vital that your user knows where they are in the application and what to expect when they take an action. Highlighting your user’s location in the nav is a great example of how to give this information in an intuitive way.

We’re going to implement this nav bar highlighting for a complex nav using Twitter Bootstrap and Ember.js. We want our nav to highlight where the user is as they navigate our application, like so:

Nav highlighting

Our end goal: Show the user their current location in our app using active classes in the nav.

How we’ll do it:

  • Use default Ember behavior to highlight the less complex nav links
  • Add logic based on the router to handle edge cases and dropdown nav elements
  • Consider easy refactoring when a Router Service is available

Say you have a component for your nav bar that looks something like this:

  {{!-- app/components/nav-bar.hbs --}}

  <div class="navbar navbar-default">
    <div class="container-fluid">
      <div class="navbar-header">
        {{#link-to 'home' class="navbar-brand"}}<img src="yourlogo"/>{{/link-to}}
      </div>

      <div>
        <!-- NAVIGATION -->
        <ul class="nav navbar-nav pull-left">

          <!-- DROPDOWN -->
          <li class="dropdown">
            <a href="#" data-toggle="dropdown" class="dropdown-toggle">
              About <i class="fa fa-caret-down"></i>
            </a>
            <ul class="dropdown-menu pull-right">
              <li>
                {{#link-to 'blog'}}Blog{{/link-to}}
              </li>
              <li>
                {{#link-to 'careers'}}Careers{{/link-to}}
              </li>
            </ul>
          </li>
          <li>
            {{#link-to 'product'}}Product{{/link-to}}
          </li>
        </ul>
      </div>

      <div>
        <ul class="nav navbar-nav pull-right">
          <!-- User Avatar and stuff -->
        </ul>
      </div>
    </div>
  </div>

Ember will, by default, add an active class to any link to the current route that’s visible. However, this won’t always work for each implementation, depending on your Bootstrap theme.

What we want is for each nav link to be highlighted when it or a child route is currently active. Unfortunately, since we have a dropdown in our nav, we can’t rely solely on Ember’s built-in highlighting for active links, since Ember won’t highlight a <li class="dropdown"> element by default.

In my Bootstrap implementation, nav styles are on <li> elements like so

  <li> {{!-- styles are here --}}
    {{#link-to 'blog'}}Blog{{/link-to}} {{!-- adds <a> to the dom --}}
  </li>

instead of on #link-to’s default anchor tag, <a>. So how do we highlight these <li> elements, including our dropdown parent element?

Utilizing Ember Built-ins:

First, newer versions of Ember allow for a tag-name option, that can be passed to #link-to. This tells Ember what type of element to create. In this case, instead of creating an anchor tag, Ember will bind a click event to the <li> that it creates. This means you can remove the element tag in your template, since Ember will create one for you. This is what’s left:

  {{link-to 'about' tagName='li'}}

With our tagName property, we’re able to eliminate a lot of those now-redundant <li> tags all over our component. We’re also able to get out of the bind we were in with our css styles applying to list tags - since our links are now list elements!

Here’s our new template:

  {{!-- app/components/nav-bar.hbs --}}

  <div class="navbar navbar-default">
    <div class="container-fluid">
      <div class="navbar-header">
        {{#link-to 'home' class="navbar-brand"}}<img src="yourlogo"/>{{/link-to}}
      </div>

      <div>
        <!-- NAVIGATION -->
        <ul class="nav navbar-nav pull-left">

          <!-- DROPDOWN -->
          <li class="dropdown">
            <a href="#" data-toggle="dropdown" class="dropdown-toggle">
              About <i class="fa fa-caret-down"></i>
            </a>
            <ul class="dropdown-menu pull-right">
              {{#link-to 'blog' tagName='li'}}Blog{{/link-to}}
              {{#link-to 'careers' tagName='li'}}Careers{{/link-to}}
            </ul>
          </li>
          {{#link-to 'product' tagName='li'}}Product{{/link-to}}
        </ul>
      </div>

      <div>
        <ul class="nav navbar-nav pull-right">
          <!-- User Avatar and stuff -->
        </ul>
      </div>
    </div>
  </div>

Now there’s one last thing to do: We need our dropdown list element, <li class="dropdown">, to be active when either blog or careers is the current route. It would be nice to use Ember’s built-in current-when option, but unfortunately, we’re using the Bootstrap dropdown, which is an <li> element, not a #link-to helper, so we’ll need to try something else.

Adding the router to your component

To solve this, let’s pass the router to our component, so we can use it to style our nav item. I’ve seen cases when router.currentPath or router.url is passed down to a component, but passing router feels closer to using a service, so I prefer this instead. Note: you can write a custom router service to handle this behavior - see the bottom of this post for more info.

  {{!-- something like application.hbs --}}

  {{nav-bar router=router}}

Now, in nav-bar.js we can add a new computed property that observes the router’s currentPath property:

aboutActive: Ember.computed('router.currentPath', function() {
  let currentPath = this.get('router.currentPath');
  if (
    currentPath === 'blog' ||
    currentPath === 'careers'
  ) {
    return 'active';
  } else {
    return '';
  }
}),

Then, in our template, we add it to <li class="dropdown">. And, we’re done! We simply change:

  {{!-- app/components/nav-bar.hbs --}}
  ...

  <li class="dropdown">

  ...

to:

  {{!-- app/components/nav-bar.hbs --}}
  ...

  <li class="dropdown {{aboutActive}}">

  ...

With different combinations of these steps, you should be able to implement a more complicated nav bar than our simple example.

In future versions of Ember, we’ll want to use the Router Service to handle conditions based on router properties. This will be a built-in service that we can use as our source of truth for where the user is located. But for now, adding the router to the component suffices.

Still Writing SQL in a Terminal?

Beekeeper is a free and full featured SQL IDE with query autocomplete, error detection, dataset previews, and result visualization. We support SparkSQL, Hive, Postgres and more!

Create your account »

Zach Foster bio photo

Zach Foster

Lead Front End. Traveler

Tech Blog Email Twitter Github