Ask the CSS Guy

Prasun asks the CSS Guy how to switch the "on" state of navigation links for dynamic pages

Prasun writes:

I have a list of links in an unordered list. What I want is that when I click a list item, its color should change, and when I click some other list item, the color of the item which I clicked previously should return to its original state.

Can you help me in this regard?

Yes, I can help.

More advanced readers, move along. This is JavaScript beginner territory, but this is one of those steps that is really rich in DOM scripting nutrients, so I'm very pleased to discuss it.

What Prasun is describing, just by itself, is the behavior of a set of radio buttons. You click one, and it takes on the selected state. Then you click on another one, and the new one now becomes selected, while the previous one returns to an unselected state.

But radio buttons are form elements, not navigational elements, and upon clarifying with Prasun, it's navigation that we're after.

This is the fundamental behavior of a site that uses ajax for navigation or tabs, where each "page" is created by loading new content into the content section of the current page.

This is a relatively easy way to get your feet wet with DOM scripting. So I'll first show how to do it with some very basic JavaScript functions. Also, I'll show how to do it with jQuery.

First, the HTML

Let's start with an unordered list of links.

<ul>
  <li><a href="#">Heartbreak Hotel</a></li>
  <li><a href="#">Blue Suede Shoes</a></li>
  <li><a href="#">Hound Dog</a></li>
  <li><a href="#">Don't Be Cruel</a></li>
  <li><a href="#">Teddy Bear</a></li>
</ul>

See example 1.

Who are we kidding? Let's see it with some basic styling - example 2.

Since we're going to be changing around selected links, let's establish what a selected state looks like, and mark one item as selected by default. I'm going to do this by applying class="selected" to one of the links.

<ul>
  <li><a href="#">Heartbreak Hotel</a></li>
  <li><a href="#">Blue Suede Shoes</a></li>
  <li><a href="#" class="selected">Hound Dog</a></li>
  <li><a href="#">Don't Be Cruel</a></li>
  <li><a href="#">Teddy Bear</a></li>
</ul>

See example 3.

The JavaScript

Here's what we want to do in English. When someone clicks a link, remove class="selected" from any of the other links, and assign it to this one.

Another way to say that is "When someone clicks a link, run a function" and later I can be more specific about what that function is. Here's how I'll put that statement in the html.

<ul>
  <li><a onclick="applySelectedTo(this);" href="#">Heartbreak Hotel</a></li>
  <li><a onclick="applySelectedTo(this);" href="#">Blue Suede Shoes</a></li>
  <li><a onclick="applySelectedTo(this);" href="#" class="selected">Hound Dog</a></li>
  <li><a onclick="applySelectedTo(this);" href="#">Don't Be Cruel</a></li>
  <li><a onclick="applySelectedTo(this);" href="#">Teddy Bear</a></li>
</ul>

The "this" mentioned in the parentheses refers to the link that is being clicked. I'm thinking ahead here - I know that I'll want to do something with the link being clicked (assign it class="selected"), so I'm going to go ahead and pass that link as a parameter to the function. The term this is great for that sort of thing.

The function will be called "applySelectedTo()", and I'll write it in just a second. It's going to do exactly what it says it will do: Apply class="selected" to whatever link is given to it. It will also live in head of the document.

Notice that in the html, I'm passing the parameter this. And when I write the function, I'm using a made up term to help tell me what this was - the word link.

<script type="text/javascript">
function applySelectedTo(link) {
  link.className = "selected";
}
</script>

Check out Example 4 to see it in action.

Oh, but we're not done

All that JavaScript function did was add class="selected" to the new link. It didn't remove class="selected" from previously selected links. We need to fix that. Also, when we click a link, it adds the hash mark (#) to our url, and if our page were really long, it would jump to the top of the page again.

We'll tackle the second problem first. That hash mark is telling me something: it's indicating that the link is behaving like a link is supposed to behave. That behavior normally means that it will find the id of whatever I specified after the hash mark and the browser will bring that part of the page in focus. But in this case, we would rather ignore the default behavior of the link, so I'm going to tell my links to ignore their default behavior when clicked.

<ul>
  <li><a onclick="applySelectedTo(this);return false;" href="#">Heartbreak Hotel</a></li>
  <li><a onclick="applySelectedTo(this);return false;" href="#">Blue Suede Shoes</a></li>
  <li><a onclick="applySelectedTo(this);return false;" href="#" class="selected">Hound Dog</a></li>
  <li><a onclick="applySelectedTo(this);return false;" href="#">Don't Be Cruel</a></li>
  <li><a onclick="applySelectedTo(this);return false;" href="#">Teddy Bear</a></li>
</ul>

Example 5

And back to the first problem - removing class="selected" from other links when a new link is selected. How do we get the link that already has class="selected", and remove the class? Well, to make it easy, I'm just going to say remove class="selected" from all links, then I'll add class="selected" to the one just clicked.

<script type="text/javascript">
function applySelectedTo(link) {
  var ul = document.getElementsByTagName("ul")[0];
  var allLinks = ul.getElementsByTagName("a");
  for (var i=0; i<allLinks.length; i++) { 
    allLinks[i].className = ""; 
  }
  link.className = "selected"; 
}
</script>

And so there you have it, Example 6 - final example.

And if you're so inclined, make it do something.

We could go much further

Ideally, you wouldn't even have an onclick attribute in the markup. That kind of thing can be inserted dynamically using more DOM scripting. It makes your html lots more readable and reusable, as well as separating behavior from structure.

Also, for kicks, here's one final example of doing the things mentioned in this article, but using the jQuery library instead of self-written scripts. In the instance that you are already pulling in a hefty JavaScirpt library into your web site, you might as well make use of it.

Comments (18)

prasun said:

thank you very much for your help.........

Petr said:

Hi, isn't possible do same thing with styling a:active ?

Chris said:

@Petr

There are probably alternate solutions out there, but given the simplicity and cross browser compatibility that the use of jQuery provides, why go through the headaches of that?

Nacho said:

Hi, I know little js.
I want to do the exact thing but with labels on a form.

Can you help me?
Thanx.

Arjan said:

The problem with your final and second final example is that the final example is much more accessible. You always should show all contents, and then hide them with JavaScript.

Also, why loop through all A elements when looking for the active element? If you set a variable named active outside your function, and then set it with a element reference, you can just access it in the function the second time. Saves a lot of time when you have a larger document.

Example:


var active = null;
function applySelectedTo() {
if (active != null) {
active.className.replace(/selected/g, ''); //remove class
}
active = this;
active.className += ' selected';
(.. stuff to display div ..)
return false; // you better put that here
}

(By the way, you can apply the onclick event to the UL and check what the target of the click was. This is called event delegation, see also http://icant.co.uk/sandbox/eventdelegation/)

For accessibility reasons you can also use the :focus state along with the :hover state, if you tab through the page you can see the menu highlight, too. (And if you really care about your users, you change both shape and color, for example by underlining when you hover/focus the button.)

Eugene said:

There's a typo just after the part where you added to 'onclick' attributes ("referes"), in case it matters at all.

f-bomb said:

Nice.

Andreas said:

I would definitely recommend moving the onclick-events to an external script as well as wrapping all the "selected-state"-functionality in its own object (if you implement Arjans optimizations) so it won't conflict with any other potential JS running on the same document.

Web Design Glasgow said:

Of course, if Prasun is talking about a list of 'pages', with a page refresh on each click, this was not the solution he was looking for. The application would have to know which item in the navigation referred to the current page, and apply the 'on' class.

XXCEMOL said:

CM said:

Hello. How would this be done without inline javascript? I'm trying to accomplish this but failing miserably.

CB said:

I tested this script and it works ok. If you replace the href"#" with real url links in the menu and then click any of the links, the class="selected" will remove to and from each links, but nothing will happen with the url to a new location because of the return false. That's the only problem I see. Is there another solution to this issue? I am not a big javascript guy.

M. Dubb said:

Good point CB, the links don't ever work.

How to make it work in the real world?

Cheers

Fraction said:

@ M. Dubb,
If you wanted to do this in the real world it would work for links that are going to hide/show content or load content via AJAX, basically anything that's going to involve more Javascript in order to show some content. This wouldn't work with a link that actually causes a page refresh or a new page to load. For that purpose you wouldn't use Javascript. Just have your server side language (PHP, Coldfusion, etc.) run a simple conditional as to which page it's on and apply the correct class to the correct link.

A lot of people have been picking apart this example and while almost all the suggestions are correct and understandable this is for Javascript beginners. Beginners being the key word.

Akmal said:

i have used this method, but the link doesn't go to the link that href provided..

e.g : <a onclick="applySelectedTo(this);return false;" href='?page=login'>Login</a>

and the page didn't go to login page.. please help..

p/s: i'm still new in web designing.. thanks..

Pasi said:

You are very good at explaining things. thanks for the help! love your site.

Aaron Damiano said:

I really don't know what I'm doing but what I want is an expandable tree menu with an on/off switch for the "selected" state. Enclosed is my code. Let me know if you can help.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<title>Example Menu</title>
<script>
var myvar;

function applySelectedTo(link) {
var li = document.getElementsByTagName("li")[0]; // get the first ul tag on the page
var allLinks = li.getElementsByTagName("a"); // get all the links within that ul
for (var i=0; i<allLinks.length; i++) { // iterate through all those links
allLinks[i].className = ""; // and assign their class names to nothing
}
link.className = "selected"; // finally, assign class="selected" to our chosen link
}
function menuinit() {
document.getElementById('m1').style.display = 'none';
document.getElementById('m1').ClassName = 'none';
document.getElementById('m2').style.display = 'none';
document.getElementById('m3').style.display = 'none';
document.getElementById('pm1').style.display = 'none';
document.getElementById('pm2').style.display = 'none';
document.getElementById('pm3').style.display = 'none';
}

function menuexpand (i) {
menuinit();
if (myvar == i) {
document.getElementById('p' + i).style.display = 'none';
document.getElementById(i).style.display = 'none';
myvar = '';
}
else {
document.getElementById('p' + i).style.display = 'none';
document.getElementById(i).style.display = 'block';
myvar = i;
}
}
</script>
<style>
#nav {
width: 140px;
font-family: Arial, Helvetica, sans-serif;
font-size: 9pt;
color: #000000;
}
#nav, #nav ul, #nav li {
margin: 0;
padding: 0;
list-style: none;
}

#nav a {
display: block;
width: 100%;
padding: 0.25em;
text-decoration: none;
}
#nav a.span {
color: black;
background-color: #ffffff;
position: relative;
}
.selected {
color: white;
background-color: #007856;
}
#nav a.span:hover {
color: white;
background-color: #007856;
}
#nav a.span img {
position: absolute;
top: 2px;
right: 2px;
border: 0;
}
#nav li {
border: none;
clear: both;
margin-bottom: 5px;
}
#nav li ul li {
font-size: 85%;
border: none;
}
#nav li ul li a {
color: black;
background-color: #f1f8e5;
}
</style>
<body onload="menuinit();">
<ul id="nav">
<li>
<a href="#" class="span" onclick="applySelectedTo(this); menuexpand('m1'); return false;" rel="nofollow"><strong>4/9</strong> Account Information<img id="pm1" alt=""></a>
<ul id="m1">

<li><a href="#" rel="nofollow">• Provident Online, Business Internet
Express and Business Internet Banking – November 30 is the last day for scheduled payments and transfers. We recommend that you print or save your scheduled payments and transfers as a reference for setting up these transactions through M&T's online services.</a></li>

</ul>
</li>
<li>

<a href="#" class="span" onclick="applySelectedTo(this); menuexpand('m1'); return false;" rel="nofollow">Menu 2<img id="pm2" alt=""></a>
<ul id="m2">
<li><a href="#" rel="nofollow">Lots and lots of text</a></li>

</ul>
</li>
<li>
<a href="#" class="span" onclick="applySelectedTo(this); menuexpand('m1'); return false;" rel="nofollow">Menu 3<img id="pm3" alt=""></a>
<ul id="m3">
<li><a href="#" rel="nofollow">Lots and lots of text</a></li>
</ul>
</li>
<p> </p>
</ul>
</body>

fud said:

is there a quick way to link to the page and have one of the selections selected already?

 

Post a comment