A Simple jQuery Fly-Out Menu

A Simple jQuery Fly-Out Menu

I haven’t made a post about jQuery in a while

Which is what made me decide to write my first post of 2011 on it. First, thank you to the people who commented here on The Web Machine in 2010. Also thanks to the over 730 RSS subscribers I have and the 25 or so followers on Twitter. This blog has been up for about a year now, and I look forward to this one. I hate all of you comment spammers though! So huge thanks to Akismet.

Anyway…a while ago I had thought about creating WordPress themes and try my hand at selling them, but it soon became apparent that the market was saturated. I don’t feel like wasting my time right now on putting effort into that. On one of those themes though, while I am not happy with the look, I created a fly-out menu using jQuery that I really liked. I thought I would share that with you since it takes less than 8 lines of code. So take a look at the demo, and then we’ll jump right in.

UPDATE 5/17/11: Removed the position:relative from the li style. It now gets the position:relative in the hover function in jQuery by adding/removing a class ‘addPosition’. This fixes the submenu appearing underneath the main menu in IE7/8. This was only noticable if you started changing the position of the submenu to be overlapping the main menu. My code here assumes you don’t do that, but with the fix it works great in IE7/8.

NOTE: In IE6, only the first level of the fly-out works. The second is cut off, but since most of us our on our way to not supporting it, I didn’t care. If you have the simple solution to fix that, let me know.

Original:
View Demo

More stripped down version (asked for in comments and the CSS now shown in sample code below):
View Demo


Now that you have seen what it is supposed to do, let’s jump in. The HTML first:

  <div id="nav">
    <ul id="navList">
      <li><a href="#">Home</a>
        <!-- This is the sub nav -->
        <ul class="listTab">
          <li><a href="#">About This Template Here</a></li>
          <li><a href="#">Flash</a></li>
          <li><a href="#">jQuery</a></li>
        </ul>
      </li>
      <li><a href="#">Blog</a>
        <!-- This is the sub nav -->
        <ul class="listTab">
          <li><a href="#">About This Template Here</a></li>
          <li><a href="#">Flash</a></li>
          <li><a href="#">jQuery</a>
            <ul class="listTab">
              <li><a href="#">About This Template Here</a></li>
              <li><a href="#">Flash</a></li>
              <li><a href="#">jQuery</a></li>
            </ul>
          </li>
        </ul>
      </li>
      <li><a href="#">About</a>
        <!-- This is the sub nav -->
        <ul class="listTab">
          <li><a href="#">About This Template Here</a></li>
          <li><a href="#">Flash</a></li>
          <li><a href="#">jQuery</a></li>
        </ul>
      </li>
      <li><a href="#">Porfolio</a></li>
      <li><a href="#">Contact</a></li>
    </ul>
  </div>

Fairly easy stuff there. It is a simple list with a byline under each item, and the first few list items have the fly out. Here is the CSS involved:

ul {
	margin: 0;
	padding: 0;
}
#nav {
	width: 330px;
	float: left;
	margin: 50px 0 0;
}
#navList li {
	list-style: none;
	margin: 0 0 10px;
	float: left;
}
 
.addPosition {
position:relative;
}
 
#navList a {
	text-decoration: none;
	color: #ffffff;
	background-color: #333;
	padding: 5px;
	display: block;
	width: 250px;
}
#navList a:hover {
	background-color: #06F;
}
#navList ul, #navList ul ul {
	display: none;
	position: absolute;
	top: 0;
	left: 280px;
	background-color: #333;
}
.listTab {
	z-index: 100;
}
#navList .listTab li {
	margin: 0;
}
#navList .listTab a, #navList .listTab a:hover {
	width: 250px;
}
#navList .listTab a {
	padding: 5px 5px 5px 10px;
}
#navList li:hover ul ul, #navList li:hover ul ul ul, #navList li:hover ul ul ul ul {
	display: none;
}
#navList li:hover ul, #navList li li:hover ul, #navList li li li:hover ul, #navList li li li li:hover ul {
	display: block;
}

The really important stuff in the CSS is where position relative is being used (the li’s), because the fly-out ‘ul’ is positioned absolutely to them. Other than that, the display none and block on the ‘uls’. Everything else is just CSS to make the menu look pretty.

Here is the jQuery:

$(document).ready(function($) {
		//Menu animation						
		$('#navList ul').css({display: "none"}); //Fix Opera
 
		$('#navList li').hover(function() {  
                $(this).addClass('addPosition');
		$(this).find('a').stop().animate({'width' : "280"});
   		$(this).find('ul:first').css({visibility : "visible", display : "none"}).show(400);
 
  		}, function() {
    		$(this).find('ul:first').css({visibility : "hidden"}).hide(400);
   		$(this).find('a').stop().animate({'width' : "250"});
                $(this).removeClass('addPosition');
			});
		});

Ok, I will admit that the first line I got from someone else. I have no idea what the hell that fixes, as I am unfamiliar with Opera (along with 3 billion other developers). It seems odd to me because my first CSS puts it at display: none anyways, but I’ve seen this before so I left it in.

NOTE: Found out Opera doesn’t hide the menus fast enough, that is why that is there.

The second line is where it all starts. The fly-out is just a hover function in jQuery. Simple yet effective. That is what I live for. I babble…the li(s) of the main ‘ul’ get the hover function. The third line says “Find the link of the ‘li’ you hovered on, stop any animation if there is any, and animate it so that the width grows to 250 pixels.”

The next line says find the first ul of the rolled over li, make it visible and set the display to none, then show it (which sets the display to block) over 4 miliseconds. I know it seems nuts to have the visibility to visible and yet display it to none, but with out the visible part, the fly-out would only show the first time then never appear again. It will work without the display set to none, but it looks like the show() works better when it is written in. I believe this has to do with show() changing the display, not the visibility.

The off hover section, does the exact opposite. The visibility is set to hidden, and the hide, which takes place again over 4 miliseconds, sets the display back to none. The final line stops any animation on the link, and then shrinks the width back to 250 pixels.

And there you have it!

A few lines of jQuery and you have a fly out menu. Most of the CSS is for styling. I have used some CSS3 here that won’t work in IE obviously, but the actual menu functions except for IE6 (see note above).

Coupon Code: webmachine

Tags: ,

22 Responses to “A Simple jQuery Fly-Out Menu”

  1. Pretty cool Jeremy. Do you know if you could make a version with CSS stripped down as much as possible? While the 8 lines of jQuery seems simple enough, the CSS looks a bit daunting in size. Mind if you could dumb it down for us? :P

    • jcDesigns says:

      No problem! The CSS show above is now for the stripped down version. I have the two demo buttons up there now. One to show the original, the other for the very basic one.

  2. Nice code.. how would you get the parent list item to keep its hover state, whilst on the child menu?

    • jcDesigns says:

      Hi David, piece of cake. Change the CSS so that ‘#navList a:hover’ is ‘#navList a.hoverState’ instead.

      Then, change this:

      $(this).find('a').stop().animate({'width' : "280"});

      To this:

      $(this).find('a').stop().animate({'width' : "280"}).addClass('hoverState');

      And this:

      $(this).find('a').stop().animate({'width' : "250"});

      To this:

      $(this).find('a').stop().animate({'width' : "250"}).removeClass('hoverState');
    • jcDesigns says:

      Jay, more info would be nice. What do you mean by it didn’t work? You couldn’t get the code to work, or the demo isn’t working in your browser? What exactly are you doing where you are not seeing the example working?

  3. Levi says:

    If the menu was on the right side of the page, how would you make the items flyout to the left?

    • jcDesigns says:

      I haven’t done it, but all you would have to do is change the ‘left’ to ‘right on the ‘#navList ul. So it would be:

      #navList ul, #navList ul ul {
      	display: none;
      	position: absolute;
      	top: 0;
      	right: 280px;
      	background-color: #333;
      }

      I don’t know what the number should be, I just left the 280px in there.

  4. james says:

    Thanks, very much. I would like to try this out. But what about making the menu items go up if the ‘ul’ is currently at the bottom of the viewable area? Otherwise, they still go down as usual?

    • jcDesigns says:

      I suppose you could change the ‘top’ number in the css to a negative number if you had to.

  5. perljunkie says:

    Great tutorial. Can you explain the part about display: none, visibility: visible in more depth? It does some illogical. I tested out what you mentioned about hover and never seeing the submenu ever again. So odd? Can you go over this part. Seems interesting and could be a source of future problems. Thanx again. This is a very good tutorial.

    • jcDesigns says:

      Oooook, thanks to you, I have now spent an ungodly amount of time trying to find the answer for this.

      People use the visibility:visible to counter the jerkiness that happens in animations. jQuery has issues with getting the width and height for an element that is display:none. Setting it to visible gives the element the width/height, then it is removed with the display:none. The show() then sets it to block.

      Without the visibility set, I believe it tries to get element’s height from its parent. This creates the jerkiness. If you actually remove both parts of the visibility on hover, and off hover, the menu will work, but if you roll over and off the menu items too fast, I believe jQuery has trouble determining the height, and everything goes nuts. By simply adding the visibility to both, it eliminates this problem.

      In CSS visibility:hidden, the element takes up the correct amount of space, but doesn’t show it. Display:none goes one further and totally removes the element.

      You need both here because the visibility allows jQuery to get the width/height, and show() and hide() only affect display NOT visibility.

  6. Brian says:

    Hello – great tutorial! I’m trying to duplicate this in a simple .html page, but am not sure where the jquery part goes. I copied the source from the simplified demo and while the demo works fine, the source pasted in an html file on mt desktop does not. What do I need to do? Running IE8 – tried in Firefox as well, so I know I must be missing something. Thanks!

  7. Thanks for your generosity in providing this really neat, nifty functionality. Much appreciated.

  8. Ali says:

    Very nice menu example. Thanks for tutorial.

  9. Ray says:

    Don’t want to say how many hours I’ve been browsing menu styles … at last something simple and perfectly effective. Thanks a stack Jeremy.

  10. Lisa says:

    Hi Jeremy,

    Thank you for your wonderful work! I’m using your menus in two places on a site I’m just finishing up: as the main navigation and as a sidebar set of menus on pages with lots of document links. It’s working really well but for one thing: on the main menu’s submenus you have to move your cursor way to the middle of the submenu in order to keep it active, and in order to be able to drill down to the next submenu link. (I don’t have that problem with the secondary menu.)

    Here’s a page where you can see both menus at work: http://www.alamedacreek.org/hidden/take-action/protecting-niles-canyon.php.

    I wonder if you can help point me to the reason why this is happening.

    By the way, I love the custom highlight color—I didn’t realize we had that capability!
    —Lisa

    • jcDesigns says:

      Ok….I noticed some things.

      Your code looks like it is using the original code i wrote. Look at the css, you will notice that I have some things that are different:

      Mine:
      #navList li {
      list-style: none;
      margin: 0 0 10px;
      float: left;
      }

      .addPosition {
      position: relative;
      }

      You are missing the .addPosition, and you have the position: relative on the li. You will also notice that the jquery i wrote has more to it than what you copied. I am thinking you copied my original code before the updated version. Let me know after you have made those changes if that corrects your problem.

      • Lisa says:

        Thanks for responding. I really appreciate it.

        I see now what I did. I wanted to remove the animation, and I guess at the same time I also removed the two lines about adding and removing .addPosition. When I added those two lines back in, and removed “position:relative;” from “#navMenu li”, voilà!

        Now I wonder why the secondary menu works perfectly well without that change! :-)

        Thank you so much for the menus and again for your help.

        best,
        Lisa

  11. Lisa says:

    I do have a follow-up question for you, in fact, which you can add as well. On the home page only http://www.alamedacreek.org/, we seem to have the problem of the sub-menu not staying visible when you try to travel to the bottom link, Contact Us. I’m wondering if it has to do with proximity to the bottom of the page, since that isn’t happening elsewhere on the site. Do you have any ideas?

    thanks,
    Lisa

    • jcDesigns says:

      Taking the position: relative off of your footer will fix that. Probably want to add a height to it as well so there is no shifting, or remove the footer all together since nothing appears to be in there anyway.

  12. Adrian says:

    Firstly thanks for this example.
    I am in the Progress of including it to a new site. I have found a strange bug that I cannot solve. Maybe you can help, as I know very Little JavaScript and even less jQuery!
    Using your example:
    Move the mouse over the “About” menu.
    The submenu opens with 3 enties.
    Move the mouse between the first and second enties in the submenu. The submenu remains the same width, there is no Animation.
    Move the mouse to the third (last) entry of the submenu and then back to the second and first. Now the submenu width is animated!
    The Animation is not really a Problem, but often some of the shadow gets left on the page and Looks horrible. I have removed the shadow but then the menu does not look so nice.

    I can reproduce this in IE10, Chome 26, Firefox 20. I hope you can reproduce the same effect.

    I think it may be a bug in jQuery and not in your code.

    Thanks for any help
    Adrian

Leave a Reply