Before I begin, I am not claiming this to be a definitive technique, in fact as a relative new comer to jQuery it can no doubt be vastly improved, however I thought I would share my technique and invite people to tear it apart if they so wish!
My aim
Create a navigation menu using UL’s and LI’s with nested UL’s that expand when their parent LI is clicked
I will be using the minified jQuery 1.3.1 library to achieve this. Get jQuery! Include this file in your HTML document’s head before following this tutorial.
HTML Markup
<ul> <li><a href="#">Item 1</a></li> <li><a href="#">Item 2</a> <ul> <li><a href="#">Sub Item 1</a></li> <li><a href="#">Sub Item 2</a></li> <li><a href="#">Sub Item 3</a></li> </ul> </li> <li><a href="#">Item 3</a></li> <li><a href="#">Item 4</a></li> </ul>
The above HTML markup will display an unordered list with 4 list items, the second of these list items having an unordered list nested within, this UL has 3 list items of its own. Each of these list items has its own anchor tag as they are to be used as navigation.
The CSS
As I will be using a class to toggle the visibility of the nested UL’s on and off, we need to write a small piece of CSS so that the class will actually do something when it is applied.
.hidden { display: none; }
Thats it!
The jQuery
To make it easier to see which piece of code does what, each line of jQuery is commented to explain what it is doing.
// code to be run when the document has loaded $(function() { // add a class of hidden to any ul's that are the child of an li. As we set any elements with a class of hidden to have the property display: none, this will now hide these nested ul's when the page loads $('ul > li > ul').addClass("hidden"); // select any a tags that are children of a ul that contains an li that has both ul and li children. Apply a function to the selected elements that will fire when it is clicked $('ul li:has(ul li)').children('a').click(function() { // toggle the class "hidden" on the child ul of the li containing the a tag that has been clicked $(this).parent().children('ul').toggleClass("hidden"); // return false so that the link is not followed return false; }); });
Ignoring the comments and the document ready, this is a small script made up of 4 lines to allow you to implement expandable navigation using nested UL’s.
… not quite finished!
The sharp eyed people reading this will realise that this script is not quite finished. What will currently happen is that the nested UL will be displayed to the user, and will only be hidden after the entire page has been loaded and the Javascript is run (I’m basing this on you placing your JS files at the bottom of the body as recommended by Yahoo). This will cause a flicker due to the content not being hidden until the entire page has loaded.
The solution to this Flash of Unstyled Content (FOUC) has kindly been documented by Karl Swedberg over atLearning jQuery, so many thanks to him for that solution.
I am going to avoid the FOUC by adding a few pieces of code to my HTML/CSS/JS, I will explain what effect each step has at the end. To begin, I added the following JS to my HTML page in the head.
<script type="text/javascript"> document.getElementsByTagName('html')[0].className = 'js'; </script>
I also added the following code to my CSS:
.js ul li ul { display: none; }
Finally I added the following line to my jQuery, before the closing brackets of my function:
$('html').removeClass("js");
Ok, now I’ll explain what this has achieved. The Javascript that I added into my head will run as soon as the page begins to load, and its purpose is to add a class of js to my HTML element. I then added a line to my CSS which will set any ULs within an LI inside an element with a class of js to display none as soon as the page begins to load, as a result we do not see the FOUC. Finally I added a line of code to my jQuery function which removes the class of js from my html element. As the nested ul’s have already had a class of hidden applied to them by my function, it is no longer required, and also the toggleClass would not display the nested UL’s even if it is fired due to nested UL’s being set to display none.
Thanks for reading!