Advertisement

Quick Tip: Give Orman's Navigation the :target Treatment

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →
This post is part of a series called Bringing Premium Pixels to Life.
Orman Clark's Vertical Navigation Menu: The CSS3 Version
Skin Orman Clark's Video Interface Using jPlayer and CSS

We recently published a great tutorial which built Orman Clark's vertical navigation menu into a flexible, jQuery powered accordion. It's actually possible to get the whole thing working without leaning on jQuery at all, and is the perfect chance to play with the CSS :target selector.


So… How?

We're going to forget jQuery and instead use the CSS :target pseudo selector to make our accordion menu expand and contract.

As a reminder here's the original jQuery demo, then here's the purely CSS approach.


The :target Pseudo Selector

You'll no doubt be familiar with URLs that look like this: http://www.w3.org/TR/selectors/#target-pseudo. It finishes off with a # sign and an element id, which is used to take the browser directly to that element. This particular url is the perfect example - it takes you to the heading with an id of #target-pseudo (feel free to go and read up).

When stitched onto the end of a url, this id is known as a fragment identifier. It's not just used to take the browser to a location on the page, it can also be used via CSS to identify the element in question.

In short; we use :target to select and manipulate the element in the fragment identifier.


Step 1: Shall We Begin?

Let's kick off by downloading the original source files from the first tutorial. The styling is already done for us, so there's little point in reinventing the wheel.

Start by opening up index.html, on line 10 you'll see that jQuery is referenced. We won't be needing that, so get rid of it.

 
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8">
		
		<title>Vertical Navigation Menu: CSS3 Coded</title>
		
		<link rel="stylesheet" href="css/styles.css">
		
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
						
	</head>
<body>

Then at the foot of the document you'll find the function which gets the jQuery accordion up and running. Get rid of all that too.

<!--initiate accordion-->
<script type="text/javascript">
	$(function() {
	
	    var menu_ul = $('.menu > li > ul'),
	           menu_a  = $('.menu > li > a');
	    
	    menu_ul.hide();
	
	    menu_a.click(function(e) {
	        e.preventDefault();
	        if(!$(this).hasClass('active')) {
	            menu_a.removeClass('active');
	            menu_ul.filter(':visible').slideUp('normal');
	            $(this).addClass('active').next().stop(true,true).slideDown('normal');
	        } else {
	            $(this).removeClass('active');
	            $(this).next().stop(true,true).slideUp('normal');
	        }
	    });
	
	});
</script>

Excellent. We now have a much cleaner basis.


Step 2: Can I See Some Identification?

As mentioned above, the :target selector will point at any element which is referred to in the fragment identifier. We therefore need to make sure our primary list items all have unique IDs, and that the anchors within them have corresponding links:

		<li class="item1" id="one"><a href="#one">Friends <span>340</span></a>
			<ul>
				<li class="subitem1"><a href="#">Cute Kittens <span>14</span></a></li>
				<li class="subitem2"><a href="#">Strange “Stuff” <span>6</span></a></li>
				<li class="subitem3"><a href="#">Automatic Fails <span>2</span></a></li>
			</ul>
		</li>

Now, when you click on the links, you'll see a fragment identifier appear in the url:


Step 3: Collapse

Currently, having removed all the jQuery bits and bobs, the accordion will be fully expanded. We need its initial state to be collapsed, so that we can reveal each sub section when the links are clicked.

Head over to the existing CSS file, we're going to add some rules to the bottom:

/*additional styles*/

.menu > li > ul {
	height: 0;
	overflow: hidden;
}

We're pointing to our submenus here; any direct descendant of the .menu list items. We're saying that their initial height is 0, and that any overflow is hidden to prevent content pushing its way into view. We now have the whole thing collapsed. And that's how it will remain unless we do something about it..


Step 4: And Expand

We want each submenu to expand, but only when we've clicked on its parent link. Let's use :target to select them:

.menu > li:target > ul {
    height: auto;
}

Put simply, this says: "See that li which is mentioned in the url? Well, change the height of the ul within it to auto". Once you click somewhere else and the id no longer appears in the url, the submenu collapses again. Try it!


Step 5: Embellishments

Visually, there are a couple of things left to do. The active state is no longer assigned by jQuery, so we need to make sure our :target list items are blue. These no longer exist:

.menu > li > a.active
.menu > li a.active span

So swap them for these:

.menu > li:target > a
.menu > li:target > a span

We'll also add a border to the bottom of our expanded submenus:

.menu > li:target > ul {
    height: auto;
    border-bottom: 1px solid #51555a;
}

OK, Orman will be happy with that :) Check out what we have so far.


Step 6: Transitions

I know. You're yelling at the screen "What about the smooth transitions?!" I'm afraid you're going to be disappointed. Unless we specify a definite height for the submenus, we can't use CSS transitions to smoothly expand and collapse our accordion. Transitions won't play nicely with height: auto. Of course, you could specify a definite height:

.menu > li:target > ul {
    height: 7.9em;
    border-bottom: 1px solid #51555a;
}

and then apply transitions:

.menu > li > ul {
	height: 0;
	overflow: hidden;
	
	-webkit-transition: height 0.3s ease-in-out;
	-moz-transition: height 0.3s ease-in-out;
	-o-transition: height 0.3s ease-in-out;
	-ms-transition: height 0.3s ease-in-out;
	transition: height 0.3s ease-in-out;
}

..but your menu will no longer be 100% flexible. You'll be restricted to a specific number (3) of submenu items. Check out the demo.


What we could do, just to smooth things out, is add a transition to another property. We can't have the height transition, so we'll make do with an opacity transition:

.menu > li > ul {
	height: 0;
	overflow: hidden;
	
	opacity: 0; 
  	filter: alpha(opacity=0); /* IE6-IE8 */
	
	-webkit-transition: opacity 0.9s ease-in-out;
	-moz-transition: opacity 0.9s ease-in-out;
	-o-transition: opacity 0.9s ease-in-out;
	-ms-transition: opacity 0.9s ease-in-out;
	transition: opacity 0.9s ease-in-out;
}

.menu > li:target > ul {
    height: auto;
    border-bottom: 1px solid #51555a;
    
	opacity: 1; 
  	filter: alpha(opacity=100); /* IE6-IE8 */
}

What we've done is set the default opacity of the submenu to 0; not only does it have a height of 0, it's now transparent too. We then also set the transitions on the default state. On the hover state, it still snaps wide open, but then the contents fade into view. A bit easier on the eye.


Step 7: Legacy Browsers

Another issue is the fact that :target isn't recognized by older browsers (Internet Explorer 8 and below), so our collapsed menu is rendered useless in those browsers. Let's apply a couple of alternative methods, which will at least make the navigation accessible.

Firstly, we'll add a conditional comment (after our initial CSS request) to haul in a secondary CSS file if the browser is Internet Explorer 8 or earlier:

<!--[if lte IE 8]>
<link rel="stylesheet" href="css/ie8.css">
<![endif]-->

Then in that file we simply repeat the rules we've just worked on, but instead of using :target, we use :hover.


.menu > li:hover > ul {
    height: 7.9em;
    border-bottom: 1px solid #51555a;
}

Again, check out the demo. Not exactly what we were after, but at least it's a backwards compatible, accessible navigation.


Conclusion

Given the choice, you'll probably still opt for the jQuery approach; it's equally accepted in all browsers (as long as JavaScript is enabled) and the effect is smoother. Still, if you weren't aware of the :target selector, this Quick Tip will hopefully have given you an insight into more CSS potential.

Advertisement