3

I have some nav links like so:

<ul id="nav">
   <li><a href="index.html">Home</a>
   <li><a href="about.html">About</a>
   <li><a href="contact.html">Contact</a>
</ul>

How can I add a CSS class called active to the opening <li> tag of the list item that contains the a href whose value matches the current url?

For example, if the current page the user is on is about.html then the nav should look like this:

<ul id="nav">
   <li><a href="index.html">Home</a>
   <li class="active"><a href="about.html">About</a>
   <li><a href="contact.html">Contact</a>
</ul>

Please note:

the urls can have additional parameters like:

about.html?foo=bar&bar=loo

so whatever is used to detect the url should not take parameters into consideration but just the page name and extensions.

I would prefer to achieve this in plain JavaScipt since I am not using jQuery for anything else on the site, but either is fine.

Edit

The index page had index.html in the url when it's landed on from another page but if the domain is types it shows as:

http://www.sitename.com/

so if no page is specified the active class should be attached to the home list's tag.

2
  • you want to do it for cosmetic reasons or for functionality? because if you are adding that class for styling, you have CSS pseudo class :active Commented Feb 3, 2011 at 14:27
  • Ok, I added the changes. Next time please post all issues at once... Commented Feb 3, 2011 at 14:33

3 Answers 3

4

jQuery:

if(window.location.pathname === '') {
     $('#nav li:first-child').addClass('active');
}
else {
    var path = window.location.pathname;
    path = path.substr(path.lastIndexOf('/') + 1);
    $('#nav li').filter(function(index) {            
        return path === $(this).children('a').attr('href');
    }).addClass('active');
}

Plain JavaScript:

var menu_elements = document.getElementById('nav').children;
if(window.location.pathname === '') {
     menu_elements[0].className += ' active';
}
else {
    var path = window.location.pathname;
    path = path.substr(path.lastIndexOf('/') + 1);
    for(var i = menu_elements.length; i--;) {
        var element = menu_elements[i];
        var a = element.children[0];
        if(a.href === path) {
            element.className += ' active';
            break;
        }
    }
}

Note: children[] is not supported by FF 3.0. If you experience any problems with children, you can substitute this with an appropriate getElementsByTagName call.

Sign up to request clarification or add additional context in comments.

1 Comment

@mplungjan: No, why should you? It uses different methods and might be a bit more compatible...
1

Simple version

window.onload=function() {
  var activeLi;
  if (location.pathname) { 
    var fileName = location.pathname.substring(pathname.lastIndexof('/')+1);
  /* just take the start - 
     not handling filenames that are substrings of other filenames 
     nor filenames with more than one dot. */
    fileName = fileName.split('.')[0]; 
    var links = document.getElementById('nav').getElementsByTagName('a');
    for (var i=0;i<links.length;i++) {
      if (links[i].href.indexOf(fileName)==0) { // starts with filename
        activeLi = links[i].parentNode;
        break; 
      }
    }
  }
  else { // no page given
    activeLi = document.getElementById('nav').getElementsByTagName('li')[0];
  }
  if (activeLi) activeLi.className="active";
}

More complex would be to ADD the active to className, but if you do not have other classes on the LI, then it is not needed - but if you use jQuery it is much simpler.

6 Comments

+1 I would remove the loop over the a elements as it can be assumed that each list element only contains one link. But you have some flaws: If the URL is like .../contact.php#foo/bar then you approach does not work anymore and it should be indexOf(fileName).
I drop everything after .html - if it changes to php or might change then I can drop whatever is after the dot - fixed as such (as well as the 'fileName', thanks. I loop over the a since that is what I can test against. Otherwise I will have to go for li[x].getElementsByTagName('a')[0] or childnodes/childnodes which can be a textnode in non-IE - hohum, so can the parentNode so yeah
@mplungjan: Ah sorry, right, you loop over all a in the list not over them in each list item. I missed that. And sorry, my example was badly chosen, I did not mean a different file ending, but when a slash appears anywhere after the filename (#foo/bar).
Ah, yes. that would be unhandy :) But so do you by the way: path = path.substr(path.lastIndexOf('/') + 1);
@mplungjan: But I only take the path of the URL, excluding the query and the hash...
|
0

//Get sub-domain url 
var currentUrl = window.location.href,
    splitUrlArr = currentUrl.replace(/\?.*/,'').split('\/');
    subDomainUrl = splitUrlArr[splitUrlArr.length-1];
//if url matches the site home url, add classname 'active' to first li
if(splitUrlArr.join('\/') == currentUrl) {
    document.getElementById('nav').getElementsByTagName('li')[0].className = "active";
}else {
    //Find the matching href and add className 'active' to its parent li
    var targetLi = null;
    var links = document.getElementById('nav').getElementsByTagName('a');
    for (var i=0; i < links.length; i++) {
        if (links[i].href === subDomainUrl) { 
            targetLi = links[i].parentNode;
            break; 
        }
    }
    if(targetLi) { targetLi.className = "active"; }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.