Get $500+ of the best After Effects files, video templates and music for only $20!
Quick Tip: Give Orman’s Navigation the :target Treatment

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

Tutorial Details
  • Topic: CSS
  • Difficulty: Intermediate
  • Estimated completion time: 10 mins
This entry is part 3 of 10 in the Bringing Premium Pixels to Life Session
« PreviousNext »

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.

Tags: Tips
Add Comment

Discussion 19 Comments

  1. Selorm Nelson says:

    Thanks a lot buddy…appreciate the code

  2. ferly says:

    I Can’t download the Source file :(

  3. Josh says:

    This has been very helpful, thank you. Orman does great work and I have enjoyed multiple executions of this menu.

  4. Well done Ian Yates. I like what you did with the :target, but not so much with the ie solution.. checked out the IE demo before i read the code, and i actually got a chock when it suddenly expand without my permission ( click ).

    How did you come across the idea of making it in CSS? The jQuery method worked in older browsers and wassent that heavy?

    Schonert

    • Ian Yates says:
      Author

      You’re right, the jQuery version absolutely isn’t heavy, and there’s no real reason why you’d choose this version over it at the moment.. It’s always important to be aware of what the options are and what’s in development, so consider this an exercise in seeing what’s out there.

      As for the IE fallback, it’s not meant to be equally functional, it just prevents the menu items from becoming inaccessible (which would make the whole thing really pointless!)

  5. Andrei says:

    Download does not work.

    • Ian Yates says:
      Author

      ..and @ferly – sorry about that, my Firefox S3 plugin doesn’t seem to have been setting permissions properly lately.. must sort that out! Try the download link again :)

  6. AdiPurdila says:

    Nice Ian!

    Just a quickie regarding the IE styling. Recently, I found a cool alternative to the extra IE stylesheets. You can check out the original post here: http://paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/

    Basically, instead of loading an extra stylesheet, you set the html class to ie6, ie7 or whatever and you style those in the main CSS file. Pretty neat :D

  7. Adam says:

    I know that this is only a demo/example, but people will copy and paste, so it should use something other than height: 0; when the lists are collapsed. Even 1px is enough, but it needs to have a size for its contents to be announced by certain ATs that ignore zero height, e.g. VoiceOver. It has been a while since I was last able to test with VO, but I believe that this is still the case.

    Thanks for another good demo, though.

  8. Ali says:

    I think once you open a navigation by clicking it, it can’t be closed by re-clicking it.
    Really loving it, thanks

  9. Tessa says:

    All are very useful tips. Especially thanks for the code snippets which is provided under each steps. Demo is particularly helpful.

  10. Ahmet says:

    thank you for the awesome tutorial, fantastic looking..

  11. Really helped me clear the concept of :target pseudo class, i think i can make use of it better now. Hoping that the new browser’s will eventually support this feature.

  12. Rod-Leg says:

    thank you very much ! I start it actually ;-)

  13. Adrian says:

    Awesome stuff

    I have just created a mobile friendly menu using this method. Add a bit of CSS media queries, and I now have a WordPress site that looks good on desktop, and switches to this menu layout when viewed on mobile

    My client will be v.happy! And so am I!

Add a Comment