Flash Without Flash: Elegant Menu Fly-out

I really like the menu setup over at Kohler. It’s a more elegant solution than regular fly-outs, but unfortunately, it’s done in Flash. Here is my version in JavaScript built with Mootools. Follow along as I describe the HTML, CSS, and JavaScript.

[Update: You can now try out and use the plugin version of this tutorial if you want the effect without the work.]

The HTML

<div id="menu">
  <ul>
    <li class="first">
      <h2><a href="#">&gt; Menu 1</a></h2>
      <ul>
        <li class="first">
          <h3>Section 1</h3>
          <p>Here are a few links:</p>
          <ul>
            <li><a href="#">Link 1</a></li>
            <li><a href="#">Link 2</a></li>
            <li><a href="#">Link 3</a></li>
            <li><a href="#">Link 4</a></li>
          </ul>
        </li>
        <li>
          <h3>Section 2</h3>
          <p>Here are some more links:</p>
          <ul>
            <li><a href="#">Link 1</a></li>
            <li><a href="#">Link 2</a></li>
            <li><a href="#">Link 3</a></li>
            <li><a href="#">Link 4</a></li>
          </ul>
        </li>
        <li>
          <h3>Section 3</h3>
          <p>Even more links:</p>
          <ul>
            <li><a href="#">Link 1</a></li>
            <li><a href="#">Link 2</a></li>
            <li><a href="#">Link 3</a></li>
            <li><a href="#">Link 4</a></li>
          </ul>
        </li>
      </ul>
    </li>
    ...
  </ul>
  <div id="menu_content">
    <p>Other content that is still <a href="#">clickable</a>.</p>
  </div>
</div>

The structure is a little more complex than in previous examples. Essentially, we have three nested lists. The first list contains each menu section. The second list contains the submenu and it’s sections. The third list contains links that will be the actual navigation. Finally, there is a div that contains content that will be visible when no link is hovered over.

The CSS

* {
  margin:0;
  padding:0; 
}
#menu {
  width:720px;
  height:400px;
  overflow:hidden;
  margin:10px;
}
#menu ul {
  position:absolute;
  z-index:99;
}
#menu ul li {
  list-style:none;
  width:180px;
  float:left;
}
#menu ul li.first {
  border:0;
}
#menu ul li ul {
  overflow:hidden;
  width:178px;
  background-color:#cc6;
  border:solid #fff;
  border-width:0 2px;
  opacity: 0.75;
  -moz-opacity: 0.75;
  filter: alpha(opacity=75);
}
#menu ul li.first ul {
  width:180px;
  border-left:0;
}
#menu ul li ul li {
  padding:10px;
  border-top:solid 1px #fff;
  font-size:0.8em;
  font-family:Arial, sans-serif;
}
#menu ul li ul li.first {
  border:0;
}
#menu ul li ul li h3 {
  font-size:1.2em;
}
#menu ul li ul li ul {
  position:relative;
  border:0;
}
#menu ul li ul li ul li {
  font-size:0.9em;
  padding:0 0 0 15px;
  line-height:1.3em;
  border:0;
  background:url(bullet.gif) no-repeat left center;
}
#menu ul li h2 a {
  display:block;
  padding:10px 5px;
  font-family:Arial, sans-serif;
  text-decoration:none;
  font-size:0.7em;
  font-weight:normal;
  color:#fff;
  background-color:#666;
  border:solid 2px #fff;
  border-width:0 0 2px 2px;
}
#menu ul li.first h2 a {
  border-left:none;
}
#menu ul li.hover h2 a {
  background-color:#663;
}
#menu_content {
  background: url(menu.jpg) no-repeat center;
  height:100%;
  padding:3em 1em 1em 1em;
  color:#fff;
}

The CSS is also a bit complex because we have so many lists to style. What’s important is setting the display property of the first list to absolute so its children will drop over the div at the bottom. The z-index has to be set to a high number to account for a bug in Safari 3. Also, the image that you see is the background image of the last div #menu_content. Notice the opacity settings in #menu ul li ul. This ensures that the background image will at least be somewhat visible if JavaScript is disabled.

The JavaScript

var Menu = {
  start: function () {  
    var menu = $('menu');
    var img_opacity = new Fx.Style('menu_content', 'opacity', {'wait': false, 'duration': 400});
    menu.getFirst().addEvents({
      'mouseenter': function () {
        img_opacity.start(0.5);
      },
      'mouseleave': function () {
        img_opacity.start(1);
      }
    });
    menu.getFirst().getChildren().each(function (el) {
      var second_list = el.getElement('ul');
      second_list.setStyles({
        'opacity': 0,
        'height': menu.getStyle('height').toInt() - menu.getFirst().offsetHeight
      });
      var fx = second_list.effect('opacity', {'wait': false, 'duration': 200});
      el.addEvents({
        'mouseenter': function () {
          fx.start(1);
          this.addClass('hover');
        },
        'mouseleave': function () {
          fx.start(0);
          this.removeClass('hover');
        }
      }); 
    });
  }
};
window.addEvent('domready', Menu.start);

Pretty simple in comparison to the rest, eh? All we do here is make an opacity effect for the div with the background image and set mouseenter and mouseleave events on the main list that trigger the effect. (I did it this way because I didn’t like how the image would fade in and out again when moving from one menu item to another.) Then we iterate through the list items of the first list and get all the second lists, set their opacity to 0 and height to extend to the bottom of the container, and create opacity effects that will be triggered on the mouseenter/mouseleave events of their parent li elements. The hover class is also added to the li element so it still looks selected when the mouse is moved into the fly-out submenu. Finally, add the start function to the domready event of the window that Mootools supplies us with. Here’s the demo again.

Modifications

When changing the width of the container, each individual menu section’s width has to be calculated manually and must account for any margin, padding, or border widths. It does take a little tweaking to get everything to line up perfectly.

I haven’t copied subtle effects, such as fading in the content of the bottom div. That can be achieved by making an opacity effect for any elements inside it, and adding a trigger to the mouseenter/mouseleave events of the main list.


23 Responses to “Flash Without Flash: Elegant Menu Fly-out”

  1. I have no name Says:

    if you’re going to post a tutorial, the results should fucking well act like the demonstration or you’re wasting my my time

  2. Sava Says:

    If it works on the demo it should work for you “No name” …

  3. online zombie games Says:

    The tutorial worked fine for me. I can’t believe how rude people are…

    Your lack of ability to implement this simple tutorial is not the fault of the author.

  4. Chris Says:

    No name, you wasted your own time :)

    Thanks for sticking up for me, everyone else.

  5. Willy B Says:

    Nice work! It looks great. Maybe I’ll use this soon.

  6. WhizKidz Says:

    It works great. Thank Chris.

  7. thegreen Says:

    The unfortunate thing is that people believe that flash and jsp cannot be melded together to produce amazing things. Chris I suggest you try to meld your idea with some flex DB funtionality.

  8. thegreen Says:

    On another note this is great for visual display-based navigation. Chris, I applaud your JS experience and for your code semantics. This could be interesting to apply with SWFObject. That way both users…flash and non-flash have a chance to experience it. Good Job!

  9. Anne-Miek Says:

    Thank you. I like it. Can I use this on a website?

  10. Chris Says:

    Thanks for the compliments, all.

    The Green, Mootools 1.2 will have a plugin to interact with Flash so you might see some experimentation from me in the future, thought I admit I don’t really know anything about Flex at all.

    Anne-Miek, Sure go ahead.

  11. click73 Says:

    Looks great, better than the flash version. Nothing makes me close a tab quicker than pages that need flash to navigate.

  12. seekertat Says:

    I love it! So much more can be done with this menu than the way mine is setup. I may try setting it up on my site, but I’m not trained in html or website design. Just worked at it over the last 5 years on my own site.

    Thank you. I think you’ve done quite well.

  13. Chris Says:

    seekertat, Since this tutorial seems to be mildly popular, I’m making a plugin version of the menu so it should be a lot easier to use if you don’t want to mess around with the tutorial. It will be up probably by the end of the week.

  14. acr Says:

    as a newbee, do I need to install the mootools framework to get this to work? I haven’t tried anything yet, just asking questions before I start….

  15. Chris Says:

    acr, you have to include it in the head before the script from this tutorial so head would look something like this:

    <head>
      <script src="path/to/mootools.js" type="text/javascript"></script>
      <script type="text/javascript">
        // script from the tutorial
      </script>
    </head>
  16. carlos Says:

    Hi
    Had anyone ever seen the tutorial for this in Flash?

  17. flippy Says:

    very easy to do in flash you can figure it out with out a tutorial

  18. Jonah Dempcy Says:

    I see you use the MooTools mouseenter and mouseleave events in favor of the DOM onmouseover and onmouseout events. MooTools has some nifty events, huh?

    I wrote an article on MooTools’ mouseenter and mouseleave events, comparing them to DOM onmouseover and onmouseout events. Here’s the URL:

    http://www.thetruetribe.com/2008/05/mootools-mouseenter-and-mouseleave.html

  19. Marc Says:

    Hi Chris,

    thx for this great plugin! One question: Would it be possible to add an “active” parameter to the script for leaving the active menu open? I try to dscribe what I mean:

    Your solution is fine for the homepage of a website, but when browsing the site I`d like to see the menu open in which content I am in. And then, when hovering the other main menu entries the active menu should fade out and fade back in again when leaving the other menu. Also the active menu shouldn`t fade out when leaving it. I tried to hack the script, but with no success…

    Thx in advance!

  20. Chris Says:

    Marc,

    I’m sure it’s possible, but like you said, it would required some hacking. What I would do is create a new variable in the Menu object that stores the active menu. Then I’d change the mouseenter and mouseleave events to check if the new variable contains an element, and if it does, fade in on mouseleave and fade out on mouseenter (if the active element and the current element aren’t the same, of course).

  21. Matt Says:

    Is it possible to have the links along the bottom instead of the top so when you mouseover the list of links popup instead of dropdown?

  1. roScripts - Webmaster resources and websites
    Trackback on March 30th, 2008 at 3:15 pm
  2. chromaSYNTHETIC Journal » Blog Archive » Elegant Menu Mootools Plugin
    Pingback on April 4th, 2008 at 3:38 pm

Leave a Reply