
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:
- Finds all of the anchor tags (via getElementsByTagName) inside of the element with the “nav” id (getElementById) – our unordered list, in this case.
- Cycles through every anchor tag that we’ve found (our “for” loop).
- 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.
- 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.


On February 20, 2008
9:38AM
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.
On February 20, 2008
12:30PM
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.
On February 20, 2008
2:16PM
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.
On February 20, 2008
2:57PM
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.
On February 20, 2008
8:48PM
sklar said:
Would manually specifying a tag have an advantage over this solution if the user had js disabled?
On February 20, 2008
11:04PM
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. :)
On February 22, 2008
5:08PM
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!
On February 25, 2008
1:47PM
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?
On February 25, 2008
1:53PM
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!
On February 25, 2008
1:53PM
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.
On February 25, 2008
1:54PM
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.
On February 25, 2008
1:55PM
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!
On February 25, 2008
1:57PM
Nate22 said:
haha, it works great. i’m retarded.
On February 29, 2008
7:06AM
Sean Nieuwoudt said:
great work!
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
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
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
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
On April 10, 2008
6:17AM
takeru said:
..but what if there’s subnavigation?
On April 10, 2008
6:17AM
takeru said:
..but what if there are subnavi?
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?
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!
On November 18, 2008
8:38AM
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.
On December 13, 2008
5:26PM
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.
On February 13, 2009
12:50AM
stephen said:
thanks for the tips–this was very helpful to a newbie.
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?
On April 26, 2009
11:03PM
andreas said:
it realy works great !!!
thanks
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.
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
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?
On December 06, 2009
12:17PM
xRommelx said:
DEMO DEMO DEMO DEMO…!!!
On January 02, 2010
1:41PM
Michael Schmidt said:
Thank you for this nifty and simple but effective script. Just what I was looking for. Excellent script, excellent explanation.
On January 11, 2010
5:40AM
tavi said:
and if you have dropdown menu ? This function activate only the subb uton and not the mother button.Please help me.
On January 11, 2010
9:58AM
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.