Click here to Skip to main content
15,881,852 members
Articles / Web Development / HTML
Tip/Trick

A Pure-CSS Menu Supporting Unlimited Levels of Both Horizontal and Vertical Menus

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
22 Jan 2016CPOL5 min read 40.7K   410   18   13
Use the provided CSS with classed DIVs to build a clean-looking web-based menu that requires no JavaScript, and supports unlimited levels of both horizontal and vertical menus.

Introduction

Yes - another "pure CSS menu" solution. This version has some nice features though:

  • Supports both horizontal and vertical orientation of all menus and sub-menus
  • Supports all combinations of orientation when moving from menu to sub-menu (that is, both horizontal and vertical sub-menus are nicely supported, regardless of whether the parent menu is horizontally or vertically oriented)
  • Unlimited levels of submenus are supported, without the need for special classes (e.g. "level-1-menu", "level-2-menu" at each level)
  • Uses classed DIVs instead of UL/LI or tables - this keeps the structure simple
  • I have fully documented the CSS so that it is easily customizable, and the structure of the HTML to build the menu is extremely simple.

Here's a sample of a menu built from this CSS/HTML that shows multiple levels and orientations:

menu

Also contained in the sample code, but not shown above, is multiple joined levels of horizontal menus. To access that, navigate to [Item 1 -- Sub Item 1 -- Sub Sub Item 3] and you'll find them.

Using the Code

There are two main steps to use this code. The first is easy - just copy the CSS into a CSS file and link it, or just include it in a <style type='text/css'>...</style> tag in your HTML file.

Here's the CSS that generated the above example... A fully-commented version of the CSS is included in the project demonstration file, so I'll only make a few comments.

First, most of the CSS that follows is relatively important, but some of it can be changed. I am assuming that the reader has the knowledge to generally know what is safe to change and what isn't. If you decide to customize, just make sure you make small changes, and then test those changes to make sure nothing major has broken. A few items that are a bit tricky - the background-color should be specified on div.menu, even if you want the menu to be transparent; otherwise, a nasty little Internet Explorer bug comes into play where the menu sometimes wants to disappear even though the mouse is still hovering over it. You can use rgba(0,0,0,0) to get a transparent menu and yet it seems to eliminate the Internet Explorer bug, but both 'transparent' and simply not setting background-color seem to let the bug occur, although it is intermittent. Another little issue with Internet Explorer is that the sub-menu positioning is a bit off. If you adjust the margin settings for the sub-menus and then test in Internet Explorer and other browsers, then you should see what I'm referring to, especially if you put a border around the sub-menus.

CSS
div.menu {
    display: none;
    color: white;
    background-color: rgba(0,0,0,1);
}
div.top {
    display: inline-block;
}
div.top.hmb > div.item {
    display: inline-block;
}
div.menu > div.item {
    white-space: nowrap;
    cursor: pointer;
    padding: 5px 10px;
    margin: 0;
    border-top: 4px solid transparent;
}
div.menu > div.item:hover {
    background-color: #262626;
    border-top: 4px solid #009ac7;
}

/*  Adjust the exact display and positions of sub-menus  */
div.hmb > div.item:hover > div.vmb {      /* Horizontal menu w/ vertical sub-menu */
    position: absolute;
    display: block;
    margin-top: 5px;
    margin-left: -10px;
}

div.hmb > div.item:hover > div.hmb {      /* Horizontal menu w/ horizontal sub-menu */
    position: absolute;
    display: block;
    margin-top: 5px;
}

div.vmb > div.item:hover > div.vmb {      /* Vertical menu w/ vertical sub-menu */
    position: absolute;
    display: inline-block;
    margin-left: 6px;
}
    
div.vmb > div.item:hover > div.hmb {      /* Vertical menu w/ horizontal sub-menu */
    position: absolute;
    display: inline-block;
    margin-left: 6px;
}

div.item:hover > div.hmb > div.item {     /* Horizontal hovered menu items */
    display: inline-block;
}

div.menu > div.item > a {
    color: inherit;
    text-decoration: none;
}

The key to making these menus work are the CSS blocks above that describe the sub-menu inheritance.  For each of the four combinations of transitions between horizontal and vertical menus, the two properties position and display control how the sub-menus will be displayed.  In all cases, we specify "position: absolute" so that the sub-menu is removed from the HTML flow, allowing the sub-menu to be displayed exactly where we want it. That CSS property setting is rather mundane, but the display attribute is more interesting. For horizontal menus, we always want sub-menus to appear below the menu item, and for vertical menus, we always want sub-menus to appear to the right of the menu item. Because the containing DIV appears immediately after the menu item text (refer to the HTML below), we specify "display: block" for horizontal menu items to force the sub-menu to drop below the menu item text. Similarly, for vertical menu items, we specify "display: inline-block" to allow the div to be positioned next to the menu item text. In all cases, some minor adjustment of the exact positioning is warranted. Often, this is accomplished by using the top/left CSS properties with position: relative. However, we cannot do that in this case because as mentioned, we need position: absolute to ensure that the sub-menus don't affect the flow of the rest of the html on the page, and thus setting top/left would then move the DIV to that exact location on the screen. Instead, we accomplish the position adjustment by setting the margin CSS property of the sub-menu DIV. Note that negative margins (a strange idea to many) are acceptable to move the DIV in the opposite direction - that is, a positive margin-top would move the DIV down, but a negative margin-top would move the DIV up.  

The second step is to build your HTML menu by using DIVs and a few classes from the CSS above. The classes are:

  • menu - This should be applied to any div that contains menu items
  • hmb / vmb - Any <DIV class='menu'> should also include either hmb (horizontal menu bar) or vmb (vertical menu bar). Choose just one.
  • top - This class just forces the menu to be displayed without the mouse hovering. As implied, this is typically used only at the "top" level of the menu
  • item - Apply this class to DIVs that contain the individual menu items, regardless of whether the item contains a sub-menu or not

There are many ways to provide interaction with the menu. Some possibilities are by using <a> tags, via onclick JavaScript events on the DIVs, or via javascript/jquery event subscription.

Here's the sample HTML that generated the example image:

HTML
<div class='menu top hmb'>
    <div class='item'>
        Item 1
        <div class='menu vmb'>
            <div class='item'>
                Sub Item 1
                <div class='menu hmb'>
                    <div class='item'>
                        Sub Sub Item 1
                        <div class='menu vmb'>
                            <div class='item'>
                                Level 4 - 1
                                <div class='menu vmb'>
                                    <div class='item'>
                                        Level 5 - 1
                                        <div class='menu vmb'>
                                            <div class='item'>Level 6 - 1</div>
                                            <div class='item'>
                                                Level 6 - 2
                                                <div class='menu hmb'>
                                                    <div class='item'>Level 9 - 1</div>
                                                    <div class='item'>Level 9 - 2</div>
                                                    <div class='item'>Level 9 - 3</div>
                                                </div>
                                            </div>
                                            <div class='item'>Level 6 - 3</div>
                                        </div>
                                    </div>
                                    <div class='item'>Level 5 - 2</div>
                                    <div class='item'>Level 5 - 2</div>
                                </div>
                            </div>
                            <div class='item'>Level 4 - 2</div>
                            <div class='item'>Level 4 - 3</div>
                        </div>
                    </div>
                    <div class='item'>Sub Sub Item 2</div>
                    <div class='item'>
                        Sub Sub Item 3
                        <div class='menu hmb'>
                            <div class='item'>Level 7 - 1</div>
                            <div class='item'>
                                Level 7 - 2
                                <div class='menu hmb'>
                                    <div class='item'>Level 8 - 1</div>
                                    <div class='item'>Level 8 - 2</div>
                                    <div class='item'>Level 8 - 3</div>
                                </div>
                            </div>
                            <div class='item'>Level 7 - 3</div>
                        </div>
                    </div>
                </div>
            </div>
            <div class='item'>Sub Item 2</div>
        </div>
    </div>
    <div class='item'>
        <a href='#'>Item 2</a>
    </div>
    <div class='item'>
        Item 3
    </div>
</div>

NOTE: Although this CSS/HTML menu does support "unlimited levels", I don't recommend going overboard. It's generally not considered good design to have menu structures that span more than 2-3 levels.

History

  • 2016-01-19 - Initial publication

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Presidio Network Solutions
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionInternet Explorer 11 problems Pin
Budsy21-Jan-16 10:09
Budsy21-Jan-16 10:09 
SuggestionRe: Internet Explorer 11 problems Pin
Michael D Bray22-Jan-16 3:07
Michael D Bray22-Jan-16 3:07 
GeneralRe: Internet Explorer 11 problems Pin
Budsy22-Jan-16 5:01
Budsy22-Jan-16 5:01 
GeneralMy vote of 3 Pin
lejuif20-Jan-16 1:10
lejuif20-Jan-16 1:10 
AnswerRe: My vote of 3 Pin
Michael D Bray20-Jan-16 1:21
Michael D Bray20-Jan-16 1:21 
GeneralRe: My vote of 3 Pin
lejuif21-Jan-16 2:54
lejuif21-Jan-16 2:54 
GeneralRe: My vote of 3 Pin
Michael D Bray22-Jan-16 3:35
Michael D Bray22-Jan-16 3:35 
QuestionGood Work Pin
jgakenhe19-Jan-16 3:36
professionaljgakenhe19-Jan-16 3:36 
AnswerRe: Good Work Pin
Michael D Bray19-Jan-16 4:08
Michael D Bray19-Jan-16 4:08 
Thank you for the praise.

You are exactly right - although I didn't mention it in the article, my motivation for writing this code was that I needed a data-driven menu. I wouldn't be surprised if there are others out there that provide the same primary capability (horizontal and vertical menus to unlimited levels) but this was a case where I felt the burn of the challenge to write one myself.
QuestionHave you consider to post this as a tip? Pin
Nelek19-Jan-16 2:47
protectorNelek19-Jan-16 2:47 
AnswerRe: Have you consider to post this as a tip? Pin
Michael D Bray19-Jan-16 3:27
Michael D Bray19-Jan-16 3:27 
GeneralRe: Have you consider to post this as a tip? Pin
Nelek19-Jan-16 4:10
protectorNelek19-Jan-16 4:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.