Intelligent Navigation Bars with JavaScript and CSS

Arnold as the Terminator

I’ve developed a trick over the years that I’ve used on a number of websites now for making my sites’ navigation bars “intelligent” or “self-aware.” By that, I mean that the navigation bar automatically knows which tab/button/whatever should be considered the currently active link, without having to manually specify a class or ID on either the body tag or on the links themselves. And since I’ve found it so useful, I thought I should share it with you, even though it does involve a smidgen of JavaScript.

I start with an unordered list that looks something like this:

<ul id="nav">
	<li><a href="/about/">About Us</a></li>
	<li><a href="/contact/">Contact Us</a></li>
	<li><a href="/archives/">Our Archives</a></li>
	<li><a href="/free/">Free Stuff</a></li>
</ul>

Then I turn the unordered list into a navigation bar, using the technique I described in my last article. I won’t delve into those details here, for the sake of brevity and my fierce, fierce (fierce!) hatred of repetition, but you might want to read the prior post first if you missed it. The navigation bar gives me tabs that expand nicely for different text sizes and respond by growing and changing color when people hover over them.

But what if you want to somehow indicate to your users which page or section of the site they’re currently visiting? For that, I use the following JavaScript magic:

function setActive() {
  aObj = document.getElementById('nav').getElementsByTagName('a');
  for(i=0;i<aObj.length;i++) {
    if(document.location.href.indexOf(aObj[i].href)>=0) {
      aObj[i].className='active';
    }
  }
}

This tiny little function does four things:

  1. Finds all of the anchor tags (via getElementsByTagName) inside of the element with the “nav” id (getElementById) – our unordered list, in this case.
  2. Cycles through every anchor tag that we’ve found (our “for” loop).
  3. Compares the “href” of the anchor tag with the page we’re currently on (document.location, which is the URL that shows up in the bar at the top of your browser) to see if the href is contained therein.
  4. If the href is a match for the page we’re on, it sets a class of “active” on the anchor tag (className=’active’).

Then we have to make sure to run the setActive script when we load the page, so that our tab gets set as soon as the page is loaded. I accomplish that with this line of JavaScript somewhere below my setActive function within the same file:

window.onload = setActive;

After that comes the CSS. Because we’ve used our JavaScript to set a class of “active” on our tab, all we need to do is style the active class. I usually do something fairly simple like the code below, which make the tab drop down from the top, applies a background color, and gives it a dark border on all sides but the top:

ul#nav li a.active {
	padding-top: 15px;
	background-color: #075a97;
	border: 1px solid #333;
	border-top: none;
}

And voila! Your navigation bar suddenly knows and indicates exactly where you are, without having to remember to specify any extra IDs on the body tag or navigation links. You can see it in action here.

As a caveat, this technique assumes that you’re not linking to a lot of commonly-nested directories in your navigation. For instance, if you have one tab going to “/blog/” and another to “/blog/archive/”, then both tabs will inherit the “active” class when you’re in the archive directory (because /blog/ is part of /blog/archive/). One way to get around this limitation would be to be more specific on the first tab – for instance, linking to “/blog/index.php” instead.

34 Comments

  1. Chris Coyier said:

    That’s awful slick, nice work Rob. Really keeps the markup clean without having to add classes inside the menu for no real semantic reason.

  2. Wolf said:

    li class=”selected” is as semantic as gets, unless you have a crafty templating system that makes the anchor a span if you are on that page.

    Not giving any indication of where people are, even just in source code is absolutely worse than adding an “”unneeded”" class.

  3. Chris Coyier said:

    @Wolf: But navigation code is generally dynamically added to individual pages so adding class=”selected”, while arguably semantic, is usually not an option.

  4. David Hucklesby said:

    Neat idea.

    Another possibility is to change the A tag to a B or STRONG. That automatically gets rid of the pointer-type cursor, and indicates current location even when styles are not applied– for Netscape 4 users, for example.

  5. sklar said:

    Would manually specifying a tag have an advantage over this solution if the user had js disabled?

  6. Rob Glazebrook said:

    Great conversation, all. :)

    @David: That would work very well, assuming your tab or whatever was specific. But if, for example, you were deep in the “archive” directory, it could be useful to still have the “archive” tab act as a link, to take the user to the root of the archive. It depends a lot on how your directories and tabs are structured.

    @Sklar: Specifying a class or ID in the navigation or body would mean that it would work even with JavaScript disabled. However, that also means you have to add an extra class or ID to every page in your site. There are benefits to either approach, really. :)

  7. Tom K. said:

    Every site I run into this same problem, and when i 1st started designing I was like,”wait there isn’t something that can detect what page you are on… I have to update the code for every page”

    What would be nice, is if it could sniff what folder the doc is in. So any html file in this folder will get this header. Anyone ever heard of this?

    Good Read!

  8. Nate22 said:

    I don’t understand. I copied and pasted your Source code into an html file, opened it up and it doesn’t work.

    Do the links have to have “?links”

    Is this entirely dependent on php or something?

  9. Rob Glazebrook said:

    Hi Nate,

    No PHP required here. You should just change the anchor tags in the unordered list to wherever you want them to go. I added “?link” to them to make them act like different pages while really existing on the same page. In practice, they’d all go to ~real~ different pages.

    You can see from the example page how the code all works together… just make sure your Javascript is between <script> tags, and set your links to pages that actually exist.

    Hope that helps!

  10. Nate22 said:

    I’m talking about this source code:
    http://www.cssnewbie.com/example/intelligent-navigation/?about

    I copied and pasted it to an HTML document and saved it to my desktop. When I opened it in the browser, the intelligent navigation doesn’t work.

    So, is it php dependent or something?
    I’m used to links that look like “< a href=”www.whatever.com/link.htm” … NOT “.

    Hope that was clear. I can’t think of any other way to explain.

  11. Nate22 said:

    Erg. I mean I’m not familiar with links that look like this:

    a href =”?about”

    Is that ?-styled link php or something?

    If so, maybe that’s why it doesn’t work.

  12. Nate22 said:

    Thanks, Rob. Huh, I wonder why it didn’t work. Will try again. Thanks! Great breadcrumb solution, btw. Been driving myself crazy with others!

  13. Nate22 said:

    haha, it works great. i’m retarded.

  14. Sean Nieuwoudt said:

    great work!

  15. On March 22, 2008
    2:00PM

    TFD said:

    Great. I just love simple but powerful solutions like this. Just like the suckerfish method:

    http://alistapart.com/articles/dropdowns
    http://www.htmldog.com/articles/suckerfish/dropdowns/

    Thanks for posting Rob.

    TFD

  16. On April 01, 2008
    3:17PM

    David Sarnowski said:

    Is it possible to see a full example page with this active. i cannot see where to place the javascripts.

    Thank you,

    Dave

  17. On April 02, 2008
    6:23AM

    David Sarnowski said:

    When I try and add this javascript to my Blogger template, I get the following error:

    Your template could not be parsed as it is not well-formed. Please make sure that all XML elements are closed properly.
    XML error message: Element type “aObj.length” must be followed by either attribute specifications, “>” or “/>”.

    any thoughts?

    Dave

  18. On April 02, 2008
    9:16AM

    Rob Glazebrook said:

    Hi David,

    You can seea a working example of the script here.

    If you look at the source of the page, you’ll see where you need to put the JavaScript: between a <script> and and </script> tag in the head of your page.

    I hope this helps!
    -Rob

  19. On April 10, 2008
    6:17AM

    takeru said:

    ..but what if there’s subnavigation?

  20. On April 10, 2008
    6:17AM

    takeru said:

    ..but what if there are subnavi?

  21. On April 24, 2008
    7:43PM

    Coxsone said:

    I am having the same problem with subnavigation as takeru. I have a Main Nav and a Sub Nav both using this method on the same page.

    If I have a Main Nav item “Products”, how would I have this remain “setActive” for muiltiple pages on Sub Nav pages such as “Product1.php”, “Product2.php”, “Product3.php”, etc.

    These multiple .php pages are “Products” pages require “Products” selected ont the Main Nav. How can I do this?

  22. On July 25, 2008
    6:07PM

    Bernhard said:

    @David and his problem with the Blogger template:

    Put the body of the script between CDATA tags like so:

    /*<![CDATA[*/

    body of smart script here...

    /*]]*/

    This hides any characters with special meaning from the XML parser and the /**/ delimiters hide the CDATA tag from the JS interpreter. Welcome to Babylon!

  23. w said:

    @Coxsone’s problem with sub navigation.

    change
    aObj = document.getElementById(‘nav’).getElementsByTagName(‘a’);

    to

    aObj = document.getElementsByTagName(‘a’);

    All though this may apply all links, not sure about this. But it works as long as the class has an “.class a.active” class attached to it.

  24. jamie said:

    Hi i tried this code and im using dreamweaver i even took the code straight off the source of ur example but it doesnt seem to be working on my site. Or in live view or preview in browser. Also i even tried to just load ur example website but that didnt even work. or the javascript the site loaded though.

  25. stephen said:

    thanks for the tips–this was very helpful to a newbie.

  26. On March 24, 2009
    3:41PM

    kurtis said:

    The trick works great, but what if you want to highlite the home page button when it is selected. If the page is named index.htm the page name dosen’t show up in the URL bar at the top of the browser. Is there a work around?

  27. On April 26, 2009
    11:03PM

    andreas said:

    it realy works great !!!
    thanks

  28. On May 14, 2009
    11:45AM

    Ro said:

    Thanks – this is great and such a simple solution! I have been using a phgp snippet which now seems like much more work.

  29. On May 21, 2009
    2:19PM

    F said:

    Wow, I have just beaten my head against the wall trying to get this to work with simple text navigation bar that becomes another color on each active page and I just can’t get it to work!? I keep trying all sorts of ways to change it but it only seems to change colors when I’m actually in the process of clicking it? I’m extremely dumb about CSS, but trying. Any other hints? Thanks for such an awesome site. I appreciate it…even if I drive myself insane. :D

  30. On May 21, 2009
    5:32PM

    F said:

    Okay, wow. ALL of a sudden it started working…EXCEPT for the “reel” page?? I have no idea why though? Help?

  31. xRommelx said:

    DEMO DEMO DEMO DEMO…!!!

  32. Michael Schmidt said:

    Thank you for this nifty and simple but effective script. Just what I was looking for. Excellent script, excellent explanation.

  33. tavi said:

    and if you have dropdown menu ? This function activate only the subb uton and not the mother button.Please help me.

  34. Rob Glazebrook said:

    Hi Tavi,

    You might want to check out this follow-up article. It uses some fancier JavaScript and it should help you with your problem.

6 Responses Elsewhere

  1. Pages tagged "javascript" said:

    [...] tagged javascriptOwn a Wordpress blog? Make monetization easier with the WP Affiliate Pro plugin. Intelligent Navigation Bars with JavaScript and CS… saved by 3 others     ilovesasuke401 bookmarked on 02/22/08 | [...]

  2. Fatih Hayrioğlu’nun not defteri » 23 Şubat 2008 web’den seçme haberler said:

    [...] CSS menü javascript yardımı ile aktif tab uygulaması. Bağlantı [...]

  3. CSS-FAQ » Blog Archive » Glazebrook proposes ways on how to create intelligent navigation bars said:

    [...] on the first tab – for instance, linking to “/blog/index.php” instead.” You can go to http://www.cssnewbie.com/navigation/intelligent-navigation/ for some cool stuff about this and other relevant things on [...]

  4. ) design collected ( :: links for 2008-03-03 said:

    [...] Intelligent Navigation Bars with JavaScript and CSS – CSSnewbie A navigation bar that automatically knows which tab/button/whatever should be considered the currently active link, without having to manually specify a class or ID on either the body tag or on the links themselves. (tags: css javascript active-navigation navigation) [...]

  5. robglazebrook.com » Blog Archive » CSSnewbie - 2 Months Update said:

    [...] I was never able to get quite back up to where I was before. The StumbleUpon people really loved my “Intelligent Navigation” article, and I’ve gotten a lot of feedback on it. There were a few other peaks in there, topping [...]

  6. On May 07, 2009
    8:04PM

    Using JavaScript to Style Active Navigation Elements « Internet Turnkey Websites said:

    [...] written about this method before, when I previously talked about building intelligent navigation bars. This technique is nice because it doesn’t rely on any JS frameworks, so you can add it to [...]

Leave a Comment