Navigation help

Hi guys and girls,

I’m new to these forums and hopefully I’m here for a while learning as much as I can. I’m starting to learn how to create I guess what you could call advanced navigation but I’m having some problems.

The navigation uses an image for the different states of the link.

I’ve got the HTML:

<ul id="navigation">
<li id="home"><a href="index.html"></a></li>
<li id="about"><a href="about.html"></a></li>
<li id="services"><a href="services.html"></a></li>
<li id="sales"><a href="sales.html"></a></li>
<li id="rentals"><a href="rentals.html"></a></li>
<li id="appraisal"><a href="appraisal.html"></a></li>
<li id="tennant"><a href="tennant.html"></a></li>
<li id="contact"><a href="contact.php"></a></li>
<li id="links"><a href="links.html"></a></li>
</ul>

And the CSS to go with it:

ul#navigation {
                list-style-type: none;
                text-align: center;
                margin: 0; 
                padding: 0;
}

#navigation li {
                text-indent: -99999px;
                background: url(images/navi.png) no-repeat;
                width: 180px;
                height: 50px;
                margin-left: 10px;
                margin-top: 10px;
		}

#navigation #home {
                background-position: 0 0;
}

#navigation #about {
                background-position: 0 -50px;
}

#navigation #services {
                background-position: 0 -100px;
}

#navigation #sales {
                background-position: 0 -150px;
}

#navigation #rentals {
                background-position: 0 -200px;
}

#navigation #appraisal {
                background-position: 0 -250px;
}

#navigation #tennant {
                background-position: 0 -300px;
}

#navigation #contact {
                background-position: 0 -350px;
}

#navigation #links {
                background-position: 0 -400px;
}

#navigation li#home a:hover {
                background: url(navi.png) -180px 0;
}

Now the navi.png file is an image with dimentions of 540px x 450px, each button is 180px x 50px and there’s 9 buttons, with the states normal, hover and active. It looks like this:

I’ve got the normal state displaying, however when hovering over the link it doesn’t change to show it’s a link. I can’t work out the hover state or active state either.

I’ve attempted posting on another forum but am having trouble understanding this concept. Hopefully someone here can offer some assistance.

So simple, thanks heaps Paul!

Don’t confuse the :active pseudo class with an indicator for the current page. :active is the moment the link is clicked and then it’s gone when the mouse is released.

The current page indicator is set by adding a class of current to the relevant item on each page as shown in your home page.


<li class="home [B]current"[/B]>

Just remove that class and place it on the correct item when on the destination page. Do that for all the other pages.

For example on the about us page the menu would be changes as follows.


<li class="home[B]"[/B]> etc...
<li class="about[B] current[/B]"><a etc.....

Ok so after creating a few of the pages, I’ve noticed that the “active” state never changes on the menu when clicking a different menu item. Not sure what the go is. Live example is here:

http://www.dbelldesign.com.au/sway

and if for some reason you can’t see the CSS:

http://www.dbelldesign.com.au/sway/sway.css

So I’ve just got to work out this: When using the second method I’ve placed the actual navigation inside a div which contains a background, I’m attempting to center the navigation within the div but am unsure which rule to apply text-align: center (assuming this will work) to.

Wow thanks for the additional reply and an alternate way to work with, that’s awesome.

Edit: When using the second method I’ve placed the actual navigation inside a div which contains a background, I’m attempting to center the navigation within the div but am unsure which rule to apply text-align: center (assuming this will work) to.

Well, your markup and approach has a few issues – though the biggest issue is I don’t see you actually DECLARING :hover in your CSS, so naturally there’s no hover.

You also are putting the background on the wrong element – you have no width/height on your anchor, so they’re not clickable, and you wouldn’t have :hover available in legacy IE.

First thing I’d do is get you some markup that actually has… TEXT in it for people browsing with images disabled or CSS disabled. That giant negative text-indent being one of the dumbest things you can do to your images-off text. As it sits you’ve got a total accessibility /FAIL/. I would then add some empty spans for gilder-levin image replacement to simplify making the hover states.


	<ul id="mainMenu">
		<li class="home"><a href="index.html">Home<span></span></a></li>
		<li class="about"><a href="about.html">About<span></span></a></li>
		<li class="services"><a href="services.html">Services<span></span></a></li>
		<li class="sales"><a href="sales.html">Sales<span></span></a></li>
		<li class="rentals"><a href="rentals.html">Rentals<span></span></a></li>
		<li class="appraisal"><a href="appraisal.html">Appraisal<span></span></a></li>
		<li class="tennant"><a href="tennant.html">Tennant<span></span></a></li>
		<li class="contact"><a href="contact.php">Contact<span></span></a></li>
		<li class="links"><a href="links.html">Links<span></span></a></li>
	</ul>

the text is for images off, the spans will be our image versions which will be placed OVER the anchor.

Now the road you were going down would have had you having to state the background-position THREE TIMES for every button. Home for example would need:

.home == background-position:0 0;
.home:hover == background-position:-180px 0;
.home.current == background-position:-360px 0;

That’s a ****load of CSS by the time you are done. I use a different approach where my absolute positioned span gets ‘chopped off’ by an overflow:hidden on the anchor. This means you only say the background-position once per element and then slide the span itself around revealing the other parts.

First order of business is to strip bullets off the ul. I’m going to assume this is a vertical menu so we don’t have to play with float and float wrapping behaviors for now.


#mainMenu {
	list-style:none;
}

Now for the LI, I like to strip them of formatting by setting them to display:inline. This prevents oddball height jumping in IE on hover, and the goofy IE7 staircase effect should you decide to float them for a horizontal menu.


#mainMenu li {
	display:inline;
}

This means most of the grunt-work has to be done on the anchor.


#mainMenu a {
	display:block;
	position:relative;
	width:180px;
	padding:16px 0;
	overflow:hidden;
	text-decoration:none;
	text-align:center;
	font:bold 16px/18px arial,helvetica,sans-serif;
	color:#FFF;
	background:#C60;
}

#mainMenu a:active,
#mainMenu a:focus,
#mainMenu a:hover {
	background:#F80; /* need this so IE 6- will actually render state change */
}

Display:block lets us set a width on them, if you are floating for a horizontal menu you can remove that. You can see I do NOT declare a height on them. Heights on elements containing text can often be problematic, and using line-height to try and center our images off text ends up with alignment issues across browsers the taller the line-height – so I just set 18px line height and 16px top/bottom padding. 16+16+18 == 50. The overflow:hidden will chop off anything in our images that are too large. I also set colors similar to the image so that again, images off users aren’t left high and dry.

You can also see I set a background color change on hover. This prevents IE from ‘sticking on’ in the hoverstate, basically ignoring the onmouseout. This also makes it pretty for images off users.

The span gets kind of interesting.


#mainMenu a span {
	position:absolute;
	display:inline; /* display state change fixes IE 7 hover bug */
	top:0;
	left:0;
	width:540px;
	height:450px;
	background:url(images/naviv.png) 0 0 no-repeat;
}

The absolute positioning ends up treating 0:0 as the upper left corner of the anchor around it since the anchor is position:relative. I set it to 0:0 as the default, then set the width and height equal to the image you are using as the replacement. This means we don’t have to slide the background-image around at all, and can slide the span around instead – again, all that extra width and height is cut off by the overflow.

To hover, we just need to slide it left 180px.


#mainMenu a:active span,
#mainMenu a:focus span,
#mainMenu a:hover span {
	display:block; /* display state change fixes IE 7 hover bug */
	left:-180px;
}

You’ll notice I set display:inline on the span, and change it to block on the hover state. IE7 can be a real dipshit sometimes on this stuff, and unless the ‘state’ changes, it will ignore the hover. Absolute positioned elements are by definition inherently ‘block’, so setting display:inline should have zero effect, but becuase it ‘changes’ from inline to block, IE7 for some reason decides to behave. (idiocy, pure and simple)

The ‘current’ state is just positioned left. Because it’s not a psuedostate the block we set on the anchor is just fine.


#mainMenu .current span {
	left:-360px;
}

The rest of it is the same idea, just using ‘top’ instead of ‘left’ for each of your unique classes.


#mainMenu .home span {
	top:0;
}

#mainMenu .about span {
	top:-50px;
}

#mainMenu .services span {
	top:-100px;
}

#mainMenu .sales span {
	top:-150px;
}

#mainMenu .rentals span {
	top:-200px;
}

#mainMenu .appraisal span {
	top:-250px;
}

#mainMenu .tennant span {
	top:-300px;
}

#mainMenu .contact span {
	top:-350px;
}

#mainMenu .links span {
	top:-400px;
}

… and that’s it. I tossed a demo copy up on my server for you to see it in action.

http://www.cutcodedown.com/for_others/airFor/template.html

I find this technique simpler to work with, and that it actually works perfect with images disabled and CSS still on is a big bonus – and it’s generally less code than stating each and every possible background-position combination.

Hope this helps – you need more explanation, ask away. I know the various IE fixes complicate what should be something simple a bit.

– edit – oops, Paul beat me to the punch… though I think he’ll get a good laugh out of my approach since it’s a lot less code and a different way of handling it.

Though he’s using :focus which isn’t exactly an ‘active’ state… which is best done either with a class on body and dual inheritance (sloppy IMHO) or using a ‘current’ class to indicate the ‘current’ page as I did.

using :focus is… well, usually I set that the same as :hover. You can see I set :active and :focus the same since that traps all keyboard navigators across all browsers. An “active” page, as in the current page being viewed cannot be set by CSS alone, you’ve got to say which one it is in the markup… and it’s also NOT what :active or :focus MEANS.

Hey thanks for the welcome and the reply. I can’t believe I didn’t see the accessibility issue with the images so thanks for pointing that one out. I’ll take a look at the code and try to understand it and if I run into any problems I’ll let you know! Cheers for the help and nice to meet you.

on a second thought, Paul forgot to mention something :wink:

Ok if I set the margin: 0 auto on the list items, it works, but then breaks the gap which I’ve put in using margin left and margin top. An example can be viewed here:

http://dbelldesign.com.au/sway/

Edit: Nevermind, fixed it. Had to put the margin 0 auto for the actual #mainMenu a, it’s working now. Thanks for all the help.

the illusion you get that the div inside a div is centred with text-align:center applied on its parent div is:

  • because divs extend 100% in their parents if a width is not specified
  • text-align:center centers (is inherited by the) inner div child inline elements like text (as Paul O’B said)

once you specify a width on the inner div you see how:

  • its box model starts from the top left corner of its parent (as before) but ends within the specified width
  • inner div child inline elements like text remain centred

If you want the nav centred within the div then the nav will need a width applied to it and use margin:auto to center it. If you want the div centered then the same approach is needed and you give the div a width and apply:margin:auto to it.

text-align:center only centers child inline elements like text (except in IE5 where it center block level elements also).

That too is a sensible approach – though often for sites I deal with ‘current’ on the menu just indicates the parent subsection when there are multiple subpages, so having it still an anchor is helpful. (even if sometimes redundant to breadcrumbs)

It’s also nice on ‘home’ when the contents of ‘home’ changes frequently as a way to prevent the user from doing a full-on refresh.

Like anything else, there are lots of ways of handling it.

Hi,

Welcome to Sitepoint.

You’ve applied the image to the list element so changing it on the anchor will have no effect :slight_smile:

You need to apply it the anchor and then you will be able to change it on hover.

However a better technique would be to place the image in an inner element (such as a span or em as shown below) which is then placed absolutely on top of the text so that it provides a more accessible technique because when images are missing the text will still be visible unlike the text-indent method.

Here’s the revised code:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
<style>
ul#navigation {
    list-style-type: none;
    margin: 0;
    padding: 0;
    width:200px;
}
#navigation li {
    width: 180px;
    height: 50px;
    margin-left: 10px;
    margin-top: 10px;
    text-align: center;
}
#navigation li a,#navigation li em {
    width: 180px;
    height: 50px;
    margin:0;
    display:block;
    position:relative;
    overflow:hidden;
}
#navigation li em{
    position:absolute;
    left:0;
    top:0;
    background:url(navi.png) no-repeat;
cursor:pointer;/* old ie fix*/
}

#navigation #home em {background-position: 0 0;}
#navigation #about em{background-position: 0 -50px;}
#navigation #services em {background-position: 0 -100px;}
#navigation #sales em{background-position: 0 -150px;}
#navigation #rentals em{background-position: 0 -200px;}
#navigation #appraisal em{background-position: 0 -250px;}
#navigation #tennant em{background-position: 0 -300px;}
#navigation #contact em{background-position: 0 -350px;}
#navigation #links em{background-position: 0 -400px;}

#navigation li#home a:hover{visibility:visible}/* IE6 fix*/

#navigation li#home a:hover em {background-position: -180px 0;}
#navigation #about a:hover em{background-position: -180px -50px;}
#navigation #services a:hover em {background-position:-180px -100px;}
#navigation #sales a:hover em{background-position:-180px -150px;}
#navigation #rentals a:hover em{background-position:-180px -200px;}
#navigation #appraisal a:hover em{background-position:-180px -250px;}
#navigation #tennant a:hover em{background-position:-180px -300px;}
#navigation #contact a:hover em{background-position:-180px -350px;}
#navigation #links a:hover em{background-position:-180px -400px;}


#navigation li#home a:active em {background-position: -360px 0;}
#navigation li#home a:focus em {background-position: -360px 0;}
#navigation #about a:active em{background-position: -360px -50px;}
#navigation #about a:focus em{background-position: -360px -50px;}
#navigation #services a:active em {background-position:-360px -100px;}
#navigation #services a:focus em {background-position:-360px -100px;}
#navigation #sales a:active em{background-position:-360px -150px;}
#navigation #sales a:focus em{background-position:-360px -150px;}
#navigation #rentals a:active em{background-position:-360px -200px;}
#navigation #rentals a:focus em{background-position:-360px -200px;}
#navigation #appraisal a:active em{background-position:-360px -250px;}
#navigation #appraisal a:focus em{background-position:-360px -250px;}
#navigation #tennant a:active em{background-position:-360px -300px;}
#navigation #tennant a:focus em{background-position:-360px -300px;}
#navigation #contact a:active em{background-position:-360px -350px;}
#navigation #contact a:focus em{background-position:-360px -350px;}
#navigation #links a:active em{background-position:-360px -400px;}
#navigation #links a:focus em{background-position:-360px -400px;}


</style>
</head>
<body>
<ul id="navigation">
    <li id="home"><a href="index.html">Home<em></em></a></li>
    <li id="about"><a href="about.html">About<em></em></a></li>
    <li id="services"><a href="services.html">Services<em></em></a></li>
    <li id="sales"><a href="sales.html">Sales<em></em></a></li>
    <li id="rentals"><a href="rentals.html">Rentals<em></em></a></li>
    <li id="appraisal"><a href="appraisal.html">Appraisals<em></em></a></li>
    <li id="tennant"><a href="tennant.html">Tennant<em></em></a></li>
    <li id="contact"><a href="contact.php">Contact<em></em></a></li>
    <li id="links"><a href="links.html">Links<em></em></a></li>
</ul>
</body>
</html>


Shout if anything is unclear but it just applies the image to the em element which is sitting on top of the text. The background pposition is then altered to show the relevant image.

Yes that’s a neat method and saves on all the background-position text.:slight_smile:

Though he’s using :focus which isn’t exactly an ‘active’ state… which is best done either with a class on body and dual inheritance (sloppy IMHO) or using a ‘current’ class to indicate the ‘current’ page as I did.

I used :active for old IE as it doesn’t understand :focus (and used :focus for other browsers) but I applied the third image to those states although I could have saved code if I’d grouped them better.

I didn’t add a current class but I usually don’t do that these days as I like the current element not to be a link as no one likes to feel stupid when they click a link that just reloads the same page.

If the menu is not part of an include then I use a strong element instead of the anchor so that it isn’t clickable.

e.g.


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
<style>
ul#navigation {
    list-style-type: none;
    margin: 0;
    padding: 0;
    width:180px;
}
#navigation li {
    width: 180px;
    height: 50px;
    text-align: center;
}
#navigation  a,#navigation  em,#navigation  strong {
    width: 180px;
    height: 50px;
    margin:0;
    display:block;
    position:relative;
    overflow:hidden;
}
#navigation em{
    position:absolute;
    left:0;
    top:0;
    background:url(http://img99.imageshack.us/img99/6899/naviv.png) no-repeat;
}

#home em {background-position: 0 0;}
#about em{background-position: 0 -50px;}
#services em {background-position: 0 -100px;}
#sales em{background-position: 0 -150px;}
#rentals em{background-position: 0 -200px;}
#appraisal em{background-position: 0 -250px;}
#tennant em{background-position: 0 -300px;}
#contact em{background-position: 0 -350px;}
#links em{background-position: 0 -400px;}

#home a:hover{visibility:visible}/* IE6 fix*/

#home a:hover em {background-position: -180px 0;}
#about a:hover em{background-position: -180px -50px;}
#services a:hover em {background-position:-180px -100px;}
#sales a:hover em{background-position:-180px -150px;}
#rentals a:hover em{background-position:-180px -200px;}
#appraisal a:hover em{background-position:-180px -250px;}
#tennant a:hover em{background-position:-180px -300px;}
#contact a:hover em{background-position:-180px -350px;}
#links a:hover em{background-position:-180px -400px;}


#home a:active em,
#home a:focus em,
#home strong em{background-position: -360px 0;}

#about a:active em,
#about a:focus em,
#about strong em{background-position: -360px -50px;}

#services a:active em,
#services a:focus em,
#services strong em{background-position:-360px -100px;}

#sales a:active em,
#sales a:focus em,
#sales strong em{background-position:-360px -150px;}

#rentals a:active em,
#rentals a:focus em,
#rentals strong em{background-position:-360px -200px;}

#appraisal a:active em,
#appraisal a:focus em,
#appraisal strong em{background-position:-360px -250px;}

#tennant a:active em,
#tennant a:focus em,
#tennant strong em{background-position:-360px -300px;}

#contact a:active em,
#contact a:focus em,
#contact strong em{background-position:-360px -350px;}

#links a:active em,
#links a:focus em,
#links strong em{background-position:-360px -400px;}

</style>
</head>
<body>
<ul id="navigation">
    <li id="home"><strong>Home<em></em></strong></li>
    <li id="about"><a href="about.html">About<em></em></a></li>
    <li id="services"><a href="services.html">Services<em></em></a></li>
    <li id="sales"><a href="sales.html">Sales<em></em></a></li>
    <li id="rentals"><a href="rentals.html">Rentals<em></em></a></li>
    <li id="appraisal"><a href="appraisal.html">Appraisals<em></em></a></li>
    <li id="tennant"><a href="tennant.html">Tennant<em></em></a></li>
    <li id="contact"><a href="contact.php">Contact<em></em></a></li>
    <li id="links"><a href="links.html">Links<em></em></a></li>
</ul>
</body>
</html>


However I do quite like your method and it is less code to implement than all those background-positions everywhere and maybe I’ll change my ways now :).