How to make a jQuery Drop-Down Menu with a CSS fall-back

The biggest problem with javascript is that we can’t completely rely on it. It can easily be disabled, and once this happens, websites should still function correctly. Javascript powered sub-navigation menus are a big problem, because if javascript is disabled, your website’s navigation suddenly goes out the door. But fear not, there is a solution!

CSS ‘popups’ do exist, but are almost never used (Well, I barely ever see them). Their only problem, is the IE6 and IE7 compatibility. The selectors used to achieve this ‘popup’ effect are not supported in IE6, however it does work in IE7 (With a few bugs, but I’ve got it working nicely in the demo). People tend to stay away from this because IE6 doesn’t support it, but I think this is a terrible reason not to use it.

If we are going to use jQuery/javascript to handle the navigation menu’s ‘popup’ effect, there will be no fallback alternative for any browser. People will just have to live with an incomplete navigation menu. So why not use CSS to achieve this when javascript is turned off? There might be no backup plan for IE6, but it does work with javascript enabled.

Let’s get started!

The HTML

<ul id="nav">
    <li><a href="#">Home</a></li>
    <li>
        <a href="#">Products</a>
        <ul class="child">
            <li><a href="#">Hard Drives</a></li>
            <li><a href="#">Monitors</a></li>
            <li><a href="#">Speakers</a>
                <ul>
                    <li><a href="#">10 watt</a></li>
                    <li><a href="#">20 watt</a></li>
                    <li><a href="#">30 watt</a></li>
                </ul>
            </li>
            <li><a href="#">Random Equipment</a></li>
        </ul>
    </li>
    <li>
        <a href="#">Services</a>
        <ul class="child">
            <li><a href="#">Repairs</a></li>
            <li><a href="#">Installations</a></li>
            <li><a href="#">Setups</a></li>
        </ul>
    </li>
    <li><a href="#">About</a></li>
    <li><a href="#">Contact</a></li>
</ul>

This is a very simple nested Unordered List. The second level Unordered List will be hidden until the parent List Item is hovered on and the third level will be revealed once the second level <li> is hovered on.

The classes of “child” and “grandchild” are added to the sub-menus so that the CSS and javascript don’t handle this at the same time. The CSS hover function specifically targets these classes, however, if the javascript is enabled, it will remove these classes in order to reduce conflict.

The CSS

/* Targeting both first and second level menus */
#nav li {
	float: left;
	position: relative;
}
#nav li a {
	background: #d6cfbd;
	border: 3px solid #4f4026;
	color: #333;
	display: block;
	margin: 0 5px 0 0;
	padding: 5px 8px;
}
#nav li a:hover {
	background: #f7f7f7;
	text-decoration: none;
}

/* Targeting the first level menu */
#nav {
	display: block;
	height: 35px;
	padding: 10px 0;
	width: 500px;
	z-index: 100;
	position: absolute;
}
#nav > li > a {
	border-top-left-radius: 10px;
	border-top-right-radius: 10px;
	-moz-border-radius-topleft: 10px;
	-moz-border-radius-topright: 10px;
	-webkit-border-top-left-radius: 10px;
	-webkit-border-top-right-radius: 10px;
}

/* Targeting the second level menu */
#nav li ul {
	background: #e1ddd3;
	border: 3px solid #4f4026;
	color: #333;
	display: none;
	margin: -3px 0 0 0;
	width: 200px;
	position: absolute;
}
#nav li ul li {
	width: 100%
}
#nav li ul li a {
	background: none;
	border: none;
	line-height: 30px;
	margin: 0;
	padding: 0 0 0 5px;
}
#nav li ul li a:hover {
	background: #f7f7f7;
}

/* Third level menu */
#nav li ul li ul{
	right: -200px; top: 0;
}

/* A class of current will be added via jQuery */
#nav li.current > a {
	background: #f7f7f7;
}
/* CSS fallback */
#nav li:hover > ul.child {
	display: block;
}
#nav li:hover > ul.grandchild  { 
	display: block; 
}

As you can see, there is only one line of CSS needed for the CSS fallback. The style is simply saying: When you hover on an <li> element, make it’s children (specifically the ul.child) display as a block.

The CSS3 properties are only applied to the <a> links that are children of the <li> which are children of #nav. This is done so that rounded corners are not given to the sub-menu’s <a> links.

Almost there, so far this is a fully functional menu with a sub-menu in IE7+ and all modern browsers.

The jQuery/Javascript

Now it’s time for some minor fade effects and support in IE6.

$(document).ready(function(){

	// Remove the class of child and grandchild
	// This removes the CSS 'falback'
	$("#nav ul.child").removeClass("child");
	$("#nav ul.grandchild").removeClass("grandchild");
	
	// When a list item that contains an unordered list
	// is hovered on
	$("#nav li").has("ul").hover(function(){

		//Add a class of current and fade in the sub-menu
		$(this).addClass("current").children("ul").fadeIn();
	}, function() {

		// On mouse off remove the class of current
		// Stop any sub-menu animation and set its display to none
		$(this).removeClass("current").children("ul").stop(true, true).css("display", "none");
	});

});

And that’s it! A cross browser sub-menu with a CSS fallback!

I can see this being done (with the fade effects) using CSS in the foreseeable future.

What say you?

  1. thak you for the post.It is just what I need for some of my projects! :)

  2. fadeh says:

    Hi, it looks like your menu is a little bit buggy. If you try to move a few time your mouse (pretty fast) over the two tabs ‘Products’ and ‘Services’, you’ll find out that the uls associated to those li are both visible, at the same time.

    The problem has something to do with mouseover/out events and the fadeIn animation itself but it looks like you can solve it just invoking a $(this).children(“ul”).stop(true, true) before this line of code: ‘$(this).removeClass(“current”).children(“ul”).css(“display”,”none”);’

    fadeh

    1. Jamy Golden says:

      Thanks fadeh, I’ve changed it to:
      $(this).removeClass(“current”).children(“ul”).stop(true, true).css(“display”, “none”);

    2. fadeh says:

      Glad it helped, I faced the same problem myself and while I was wondering why, looking for a solution on google, i finally came across your site (meanwhile I found out that was a common problem, many other menu, with almost same features, are affected aswell) and it gave me the boost to find a solution :D

      Sorry for my bad english and keep it up with the good work.

      Bye,

      fadeh

    3. mansoor says:

      this very esay to learn thanks guys

  3. Jacob says:

    Hey quick question, how do make it so a child hove shows another child. Did that make sense? 0_0

    1. Jamy Golden says:

      Do you mean having a third level menu? Like this:

      <ul>
      	<li></li>
      	<li></li>
      		<ul>
      			<li></li>
      			<li></li>
      		</ul>
      	</li></li>
      	<li></li>
      </ul>
  4. Jacob says:

    I’m trying to have it so that when you hover over a child in the nav bar another list pops up to the right, I figured I would have to do something to the css? Here’s the portion of my unordered list.

    /* Code not formatted */

    Thanks for your help and speedy response =).

    1. Jamy Golden says:

      I’ve updated the tutorial and it now includes a third level menu.

      Changes:
      The xHTML has added markup

      The CSS has 2 extra lines:
      #nav li ul li ul{right: -200px; top: 0;}
      #nav li:hover > ul.grandchild { display: block; }

      and the javascript has 1 added line:
      $("#nav ul.grandchild").removeClass("grandchild");

      I hope this helps :)

  5. Jacob says:

    Thank you very much! I love what you’ve done here, consider yourself..bookmarked? =)

    1. Jamy Golden says:

      =) Thanks. I hope you find other/future articles just as useful.

  6. Milos says:

    Hi, great post thank you! ;-)

    I have a problem with your JS in IE 6 and 7.

    When the page loads, IE displays this warning message:


    A Runtime Error has occurred!
    Line: 1
    Error: Object expected

    I can`t understand what is the problem. Do you have any ideas?

    1. Jamy Golden says:

      Thanks :)

      I’m not getting the same errors on my examples.

      What are you using to view via IE6 and 7 (Any specific emulator?) and do you have a live example of the error?

    2. Milos says:

      I’m not using emulators, I’m using real IE6 and 7 browsers from different computers.

      This is the live example of my problem: Sajam kozmetike

    3. Milos says:

      I have found a solution for my problem!

      The problem was caused by bad place for including your JS. If $(document).ready() appears before the jQuery script include tag, the error occurs.

      When I included your JS after jQuery script include tag, the IE error disappears.

  7. This is simply fantastic thank you for this!

  8. hugo says:

    Hi there, this is really good, im just getting into css and i find it really helpful. It all works well, the only problem is that when I hover on the link for the first time it appears instantly, I am forced to hover a second time to make it fade in.
    Can you suggest possible causes for this,
    Thanks.

    hugo

    1. Jamy Golden says:

      Strange – What browser is that happening with? It seems to work fine for me on Chrome and Firefox.

    2. Jonathan says:

      It is probably caused by your CSS.

      Try delete the line ‘display: none’ in your sub menu CSS and tweak the first line of jQuery provided in this tutorial to:

      $(“#nav ul.child”).removeClass(“child”).css(“display”, “none”);

      Otherwise if you follow strictly to this tutorial, the menu should work fine.

  9. Neha says:

    Thanku Thanku Thanku Thanku Thanku Thanku Thanku Thanku Thanku soooooooooooooooooooooooooooooooo muchhhhhhhhhhhhhhh

  10. Neha says:

    plz tell me why the speaker’s sub menu can’t work?

  11. WHOAMi says:

    Thank you for sharing this great peace of yours! :)

  12. fahim says:

    thank’s for soliution

  13. macobse says:

    The dropdown menu is very fast I cannot make it slow.when the mouse is out the dropdown
    will hide automatically. please somebody help me?

    1. Use this javascript instead of the original from this article.
      Hope you enjoy it

      $(document).ready(function(){
      
      $('#nav li').has('ul').hover(function(){
      		$(this).addClass("blunne-topnav-linkhover").children("ul").stop(true).fadeTo(350,1);
      	},function(){
      		$(this).removeClass("blunne-topnav-linkhover").children("ul").stop(true).fadeOut(350,0);
      	});
      
      });
  14. Mo says:

    Hi,

    First off this great what you have put up here..thank you for that.

    I am trying to implement a 3rd level menu or “grandchild” as you listed it above, however, it is not working.

    Here is my code:

    <a href="#" rel="nofollow">Menu Item 1</a>
        <a href="#" rel="nofollow">Child Of Menu Item 1</a>
            Grandchild of Menu Item 1

    I was hoping to see grandchild of menu item 1 display when I hovered the mouse of the child of menu item 1 but this is not happening.

    Thank you very much for your support.

    -Mo

    1. Jamy Golden says:

      The css would be (If you disable the javascript and add the following CSS instead of the “/* CSS fallback */” part it should work correctly):

      #nav > li ul { display: block; }
    2. Mo says:

      I deleted the content that was under /*CSS fallback*/ and added the line you mentioned but that did not work. This actually opened up all of the child menu items at once when I loaded the page.

      I am not using the the Javascript listed above. I should mention one thing though. I call your menu using the “include” php function as so:

      So your html code and CSS call are all in menu.php. I’m not sure if this has anything to do with it.

  15. spo says:

    Thanks human!!
    Really helped!!
    Nice job!

    1. Jamy Golden says:

      Thanks :) I’m glad you found it useful!

  16. Amir Habibi says:

    Thank you for sharing this.

  17. Hi Jamy Golden,

    I am very glad with this code, I use it now in an Android app.
    Is it possible to remove the bullets caused by ?

    Thank you very much!

    Marcel

    1. Extra information: I has written my code in html5 which is translated into App code. Service from http://www.appsgeyser.com

    2. I have found the CSS Plus in which my problem was solved.

      Thank you very much.

      Marcel Penders

    3. Jamy Golden says:

      Haha great. Let me know if you run into any more problems.

  18. Hamide says:

    Thanks, It really helped me

  19. Jamy – I just wanted to let you know that I created this menu and was able to style it just the way I wanted it and I didn’t use any JQuery at all and it works! I was a bit confused as to where to put the Jquery code but it was working anyway so I didn’t add it. Thank you!