Auto-selecting navigation

June 17th, 2006

This article offers an alternative to the laborious task of coding up which <li> or <a> navigation tags need the 'selected' class (or however you concoct the solution).

The Problem

Any web developer, or designer that wants to offer some visual indication which page the we are on, they will probably use a combination of 'selected' and possibly 'unselected' classed on the link or tab.

The developer's task is to ensure that the right link or tab is highlighted when we are viewing the page.

The Old Solution

The way that I have solved this problem in the past (being a developer), either using Perl or PHP, would be to use a hash to track which page is selected, and when I print each link or tab, I would reference the hash with the key value of the current element name.

For example (in PHP, in Perl):

$selected = Array('articles' => ' class="selected"');

foreach ($links as $link) 
{
  echo '<a href="' . $link->href . '" ' . 
    $selected[$link->name] . '>' . 
    $link->name . '</a>';
}
my $selected = { articles => ' class="selected"'};

foreach my $link (@links)
{
  print qq{<a href="$link->{'href'}" 
    $selected->{$link->{'name'}}>$link->{'name'}</a>};
}

The New Solution

The new solution really comes about, because as a developer I really want to avoid doing the dull work, and separating the layout code from the application code is what we all really want to do.

In this solution, you create all the HTML, either through static files, or as you like, and add some JavaScript seasoning to make the the navigation automatically highlight itself.

Prerequisites

  1. You are happy that when we don't have JavaScript enabled, the navigation doesn't highlight. I justify this by the simple fact as users, we're more interested in the page content, than which page link is highlighted.
  2. The navigation sits cleanly in one DIV under one ID.

The Code

Notice that this is we've done away with our backend solution altogether.

<h3>Info</h3>
<ul id="sidebar_content">
  <li><a href="/info/news">News</a></li>
  <li><a href="/info/articles">Articles</a></li>
  <li><a href="/info/resources">Resources</a></li>
  <li><a href="/info/about">About</a></li>
</ul>

Now we add our dash of JavaScript spice...

function select_nav() {
  var nav_links = document.getElementById('sidebar_content').getElementsByTagName('a');
  var selected = location.pathname;

  for (var i = 0; i < nav_links.length; i++) {
    var link = nav_links[i].pathname;
    if (link.substring(0, 1) != '/') link = '/' + link; // fiddle IE's view of the link
    if (link == selected) nav_links[i].setAttribute(cattr, 'selected');
  }
}

window.onload = function() {
  select_nav();
};

...and Bob's your father's brother. The link will automatically select itself.

For those of you using jQuery, here's the same solution, but on a lot less lines (you've got hand it to those jQuery chaps):

$(function(){ if (location.pathname.substring(1))
  $('#sidebar_content a[@href$="' + location.pathname.substring(1) + '"]')
    .attr('class', 'selected')
});

Updated 25th Sep '06 - with thanks to Kevin, Fallo and Steve.

Note: the a[@href$= part is saying that any anchor tag whose "href" attribute value ends exactly with the string location.pathname.substring(1).

Feel free to add comments, suggestions, ask questions, point out any errors or suggest a better alternative.

Comments

New comment Kelvin Luck said on September 4, 2006:

Nice solution - very clean, especially in jQuery!

How about changing:

window.onload = function() {

to:

$(document).ready(function() {

in the jQuery version? It seems a little more jQueryish and you don't need to worry about overwriting or being overwritten by any other document.onload scripts (which the current script could do),

Cheers,

Kelvin :)

New comment Fallo said on September 19, 2006:

With the jquery 1.0 +

$(document).ready(function(){
if (location.pathname.substring(1))
$(’#linklist a[@href$=”’ + location.pathname.substring(1) + ’”]’).attr(‘class’, ‘here’);
});

change the .set to .attr

New comment Steve said on September 23, 2006:

$(document).ready(function(){}) is the same as $(function(){}).

So we can even shorten it further.

$(function(){ if (location.pathname.substring(1)) $(’#linklist a[@href$=”’ + location.pathname.substring(1) + ’”]’).attr(‘class’, ‘here’);
});

New comment Gios said on October 5, 2006:

Here is another way:

$(function(){
$(”#menu”).find(“a”).each(function(){
if( location.href.indexOf(this.href) != -1) {
$(this).addClass(“here”);
}
});
});

New comment federico said on October 20, 2006:

hello, i have tried your jquery script but it is not working… is it because my links are in this format? Any ideas?

<ul id=”mainmenu”>
<li><a href=”/index.php?page=home”>Home</a></li>

New comment Adam Messinger said on November 6, 2006:

You can actually do this without any JavaScript at all, using only CSS. Roger Johansson has a tutorial.

New comment Elisa said on November 29, 2006:

Hi, I tried the script but it only works with Internet Explorer 7. With other browsers it doesn’t work… That’s right?

New comment Remy said on November 29, 2006:

@Elisa – to honest I’ve not tried the script in IE 7 yet – but I can confirm that it works in Safari and Firefox. I use it for my own sidebar links and it works fine for me.

Maybe you could post an example?

New comment Michael said on December 4, 2006:

The method referenced by Adam above using only CSS only works for the top level of navigation. If you have multiple levels that you need highlighting to work for, you’d need to add a lot more css and html to your code.

The method in this tutorial is very elegant. Thanks

New comment Manfred Morgner said on December 10, 2006:

The ‘pur CSS solution’ does not separate the content from te layout! Therefore, this way only hides the ‘Old Style Problem’.

New comment Voyagerfan5761 said on January 23, 2007:

Ah! Wonderful, especially since I already had jQuery for sortable tables. Two lines, a little modification to the CSS stylesheet, and BOOM! Thanks a lot!

New comment mediavrog said on July 7, 2007:

Well as this JS method being not unobstrusive i don't like this approach and would prefer a pure css solution like Roger Johanssons.
Another question: what if the pathname ends with an anchor tag like #myanchor - will the a href still match the location.pathname?

New comment jim said on July 10, 2007:

Here's an idea I was playing with.... I wanted this to work for subpages of a site as well. This pulls of the first folder of the link: (sitename.com/firstfolder/subpage/ would check for firstfolder only)


$(function(){ if (location.pathname.substring(1))
  $('#mainlinks a[@href*="' + location.pathname.substring(1,location.pathname.indexOf("/",1)) + '"]')
    .attr('class', 'selected')
});

New comment Roman said on July 14, 2007:

Hello! Great idea! But how about second and third level menus?

New comment brwalias said on October 29, 2007:

Hi,

I'm new to JQuery but love the simplicity of it and love working with it so far. How would I go about highlighting 2 navigation areas on a page if I have a main and subnav?

New comment francesco said on December 21, 2007:

hi ,
everybody. I am trying to make work your great script but without success...maybe because I have applied the id to a div tag instead of a ul tag? can you please give me a working demo using javascript? (I have also tried with jquery but nothing changes...)
my attempts can be seen here:
http://www.viabenedicti.it/en
if you click on Places of worship and choose a submenu item it should become red...but the class active is not applied... :( thanx a lot!

Post your own comment
  • This comment form supports limited Markdown entry.
  • Please wrap code examples in <pre><code>
  • Please note that your e-mail will not be displayed on this page. We will never pass on your details and your information will be kept private.
Back to top