trovster.com

The personal website of Trevor Morris

Targeting the Navigation

A common practice on websites is to visually identify the current section or page by altering the appropriate link in the navigation area.

Ideally, links in the navigation should not link to their current page, so should be removed. This allows the area and text to be styled differently from the rest of the navigation. However, a section which is linked to within the navigation, may have numerous pages within it. Highlighting that current section differently from the rest is still important.

There are a numerous methods to target the current section within the navigation using CSS.

Explicitly active

A common solution is to add an id to the anchor for the current page. This solution is adequate but falls short in many areas. A slight alternative is to add the id to the list item. This gives more flexibility and styling opportunities as you can target both the <li> and <a>.

A problem with this solution is that it uses an id to target the active state. If your site has a sub-navigation this method can’t be used, as ids are unique. Therefore the use of class is more appropriate in this situation. A convention for naming the id/class when targeting the current page is to use “active”.

The mark-up for this method is simply:

<li class="active"><a href="http://www.example.com"> Example<a></li>

And the CSS:

li.active {}
li.active a {}

The main problem with this method is that it requires the navigation mark-up to be changed on every page. This can be difficult to implement if you are using a server-side technology to include the navigation.

Using Your Body

Another solution uses more verbose mark-up; adding ids to each list item of the navigation. For example:

<ul id="navigation-main">
  <li id="home"><a href="/">Home</a></li>
  <li id="about"><a href="/about/">About</a></li>
  <li id="contact"><a href="/contact/">Contact</a></li>
</ul>

To target the appropriate anchor, an id is given to the element. This id changes based upon the section you’re viewing. A simple, yet powerful, CSS selector is then used to style the anchor accordingly.

If you are visiting the about section, the following addition would made to the page mark-up:

<body id="about-page">

And the CSS to target this link would be:

body#about-page ul#navigation-main li#about a {}

For the navigation example above, three different selectors need to be used, these can be combined to form one selector:

#home-page #home a,
#about-page #about a,
#contact-page #contact a {}

Using Your Body: Revised

Although, the “using your body” method is pretty flexible it is difficult to use if you have sub navigation. Because the id used on the <body> is used to target the main navigation, this leaves no method to target the sub navigation link.

A revision to this method allows for more than one active state to be targeted per page.

Instead of using ids on the <body> and list items, a class can be used. If the <body> id is simply replaced with a class and the selectors changed appropriately, the method still works perfectly for targeting the main navigation.

Classes can have multiple selectors. Using classes on the <body> gives the ability to target more than one active state per page. Classes on the list items allow the navigations to be repeated, in the footer for example.

Take the following mark-up:

<body class="about company">
<ul id="navigation-main">
  <li class="home"><a href="/">Home</a></li>
  <li class="about"><a href="/about/">About</a></li>
  <li class="contact"><a href="/contact/">Contact</a></li>
</ul>
<ul id="navigation-section">
  <li class="site"><a href="/about/site/">About the site</a></li>
  <li class="company"><a href="/about/company/">About the company</a></li>
</ul>

Now we have the ability to target both the primary and secondary navigation by using the following selectors:

.home .home a,
.about .about a,
.contact .contact a,
.site .site a,
.company .company a {}

Any combination of the selector above can be used. The example above targets all the active pages on both the primary and secondary navigation. You can of course split the selector up to style the primary and secondary navigations differently.

Note: Beware when using the classes in CSS because you could be styling both the body and the list item. To avoid this issue make sure you use specific element selectors, or use a navigation container, for example:

li.className {}
#navigation .className {}

Other benefits

This method allows for a lot more flexibility than with the basic “active” method. Because all of the list items contain classes it allows you to use image replacement techniques for each of the anchors individually. The mark-up on the navigation remains untouched, which is good if you’re dymanically including it on every page.

Finally, the classes on the <body> allow you to style a whole section or page slightly differently. This could be used to colour code certain sections or add page-specific styles without extra stylesheets or ids within the page.

Increasing usability

…a special case of a guideline that applies to all website or intranet pages: never have a link that points to the current page.

Jakob Nielsen

However, the methods discussed above do not remove the link of the active page from the mark-up. All these methods simply add stylistic differences between pages and sections. Great for a visual key, but could cause confusion if the link is clicked and the page doesn’t change.

A solution is to remove the section link from the markup, however, this may be problematic if you’re including the navigation dymnically.

JavaScript to the rescue.

Before we start, the script requires a few mark-up prerequisites.

  • The class on the has a specific ordering — the last class is the current page.
  • Classes are on the list items of the navigation and not the anchor itself.
  • Finally, the classes on the list items are the same as the classes used on the body.

First a little bit of checking to see whether the body has a class name, if not then we don’t need to run the script.

var className = document.getElementsByTagName('body')[0].className;
if(!className) return false;

Classes are separated by spaces, so we split them into an array. If there is only one class specified then this array won’t work. If there is an array then we want the last class. Because arrays in JavaScript start at zero we want the length of the array minus one. If there isn’t an array then use the only class.

var classNameArray = className.split(' ');
if(classNameArray) var skipLink = classNameArray[(classNameArray.length)-1];
else skipLink = className;

We now loop through all the anchors on the page. (This can be ammended to loop through only the navigation links, using the navigations id.) The script checks the parentNode, the <li>, for an class name, if there isn’t one, the loop skips to the next anchor.

var anchorArray = document.getElementsByTagName('a');
for(var i=0; i<anchorArray.length; i++) {
  if(!anchorArray[i].parentNode.className) continue;

Finally, we try and match the <body> class name with the <li> class name. If there is a match then we set the anchor link to a hash and add a negative tabindex.

if(hasClass(anchorArray[i].parentNode.className,skipLink)) {
  anchorArray[i].setAttribute('tabindex','-1');
  anchorArray[i].href = '#';
}

Note: Although, a negative tabindex is invalid according to the W3 HTML specification, (it was however, suggested in an old HTML forms working draft) it is a useful addition. Some user agents have implemented a negative tabindex which removes the element from the tabbing order completely — very useful in this situation.

Putting it all together

Here is the complete JavaScript. Simply include this and add it onload.

function skipActive() {
  var className = document.getElementsByTagName('body')[0].className;
  if(!className) return false;

  var classNameArray = className.split(' ');
  if(classNameArray) var skipLink = classNameArray[(classNameArray.length)-1];
  else var skipLink = className;

  var anchorArray = document.getElementsByTagName('a');
  for(var i=0; i<anchorArray.length; i++) {
    if(!anchorArray[i].parentNode.className) continue;  
    if(hasClass(anchorArray[i].parentNode,skipLink)) {
      anchorArray[i].setAttribute('tabindex','-1');
      anchorArray[i].href = '#';
      break;
    }
  }
}

Note: This script requires the hasClass function by Gavin Kistner.

About

My name is Trevor Morris & I am a movie-loving, mountain bike-riding web developer from the UK. Currently, freelancing at Surface.

Latest tweet

Occasionally I hear a sound which came from my computer and am totally confused. Then I realise it’s one of those annoying web chat popups.

Subscribe

You can keep up to date with my blog or movie ratings by following the feeds: