Solution For Very Long Dropdown Menus

Chris Coyier //

I like to be confident with post titles, but the reality in this case is a *possible* solution for very long dropdowns. The problem with long dropdowns is that the dropdown itself can go below the "fold" of the website. That is, below the visible area of the browser window. So in order to access those menu items down below, you need to scroll your browser window. For those of us with scroll wheels of some kind on our mice (mouses?), it's not a big deal. For those without, those lower menu items are totally inaccessible, because to use the browser scrollbar means mousing off the menu (and probably having it close).

 

View Demo   Download Files

 

I first saw this idea in effect on the Media Temple account center page. They have a dropdown for "Domains" in which every single one of your domains hosted with them is listed. This can be a very long list. Media Temple had implemented a technique where as you scroll down, the menu actually moved in an accelerated fashion.

Of course, as I go to take this screenshot I find that the cool solution they had complimented isn't there anymore. Kind of makes me wonder what kind of trouble they had with it, but oh well, let's roll with it. Here an attempt at visualizing the idea:

jQuery Makes it Tick

I'm going to dump the code all in here just for quick reference. I commented it, so it should be fairly easy to follow. It's over 60 lines, but don't worry too much, it's fairly simple.

  1. Set a maximum height to the dropdowns
  2. On hover, reveal the submenu
  3. Calculate a speed multiplier based on the height of the submenu
  4. Watch for mouse movement in the menu
  5. Scroll the menu with the mouse movement, based on the multiplier
  6. On mouse out, close the menu
var maxHeight = 400;

$(function(){

    $(".dropdown > li").hover(function() {
    
         var $container = $(this),
             $list = $container.find("ul"),
             $anchor = $container.find("a"),
             height = $list.height() * 1.1,       // make sure there is enough room at the bottom
             multiplier = height / maxHeight;     // needs to move faster if list is taller
        
        // need to save height here so it can revert on mouseout            
        $container.data("origHeight", $container.height());
        
        // so it can retain it's rollover color all the while the dropdown is open
        $anchor.addClass("hover");
        
        // make sure dropdown appears directly below parent list item    
        $list
            .show()
            .css({
                paddingTop: $container.data("origHeight")
            });
        
        // don't do any animation if list shorter than max
        if (multiplier > 1) {
            $container
                .css({
                    height: maxHeight,
                    overflow: "hidden"
                })
                .mousemove(function(e) {
                    var offset = $container.offset();
                    var relativeY = ((e.pageY - offset.top) * multiplier) - ($container.data("origHeight") * multiplier);
                    if (relativeY > $container.data("origHeight")) {
                        $list.css("top", -relativeY + $container.data("origHeight"));
                    };
                });
        }
        
    }, function() {
    
        var $el = $(this);
        
        // put things back to normal
        $el
            .height($(this).data("origHeight"))
            .find("ul")
            .css({ top: 0 })
            .hide()
            .end()
            .find("a")
            .removeClass("hover");
    
    });
    
    // Add down arrow only to menu items with submenus
    $(".dropdown > li:has('ul')").each(function() {
        $(this).find("a:first").append("<img src='images/down-arrow.png' />");
    });
    
});

HTML & CSS

I hesitate to dump a bunch of HTML and CSS code in here because it just isn't very interesting. You can download the code anyway, so take a peak there. It's just a normal semantic nested unordered list and some simple very basic styling.

Testing

I tested it in everything all the way down to IE 6 and it seemed good.

 

View Demo   Download Files