10

EDIT: see also my own answer below (2016)


For example:

<ul>
    <li class="first"><a href="#" title="">Home</a></li>
    <li><a href="#" title="">Chi siamo</a>
       <ul><li><a href="#" title="">item1</a></li><li><a href="#" title="">item2</a></li></ul>
    </li>
    <li><a href="#" title="">Novità</a></li>
    <li><a href="#" title="">Promozioni</a></li>
</ul>

Then styled:

/* level 1 Main menu ----------------------------------------- */

#main-menu > ul{ list-style:none; }

#main-menu > ul > li{
    float:left;
    display:block;
    position:relative;
    margin-left:1em;
}

#main-menu > ul > li.first{
    margin-left:0;
}


/* sub Main menu ----------------------------------------- */

#main-menu > ul > li ul {
    position: absolute;
    z-index:1000;
    display:none;
    left:0;
    top:28px;
}

#main-menu > ul > li:hover ul {
    display:inline-block;
}

#main-menu > ul > li ul li{
    float:left;
    display:block;
    list-style:none;
}

ok. So, I've got the main menu that shows up horizontal. I also want the submenu to show horizontal. But for some reason, the ul box do not resize to reach the total li tags width, so it remains vertical. The parent ul do not suffer of this problem. I could make it work by simply adding a proper width to the child ul, but this is not the way I wanna use.

Any idea?

Luca Reghellin
  • 7,426
  • 12
  • 73
  • 118
  • 4
    I suggest the use of id and class selectors instead of 'ul > li ul' or '#main-menu > ul > li ul li' – thedev Jan 14 '11 at 13:05

6 Answers6

4

It's important to have the :hover twice if you use position absolute; 1st on the li, that triggers the display: block, then on the ul that is shown on trigger.
And then keep positioning and styling separate: I styled the a and not the li

See here: http://jsfiddle.net/HerrSerker/T8x2r/2/

#main-menu > ul > li > ul:hover,
#main-menu > ul > li:hover > ul {
    display: block;
}

Should work with float:left also http://jsfiddle.net/T8x2r/3/

kapa
  • 77,694
  • 21
  • 158
  • 175
yunzen
  • 32,854
  • 11
  • 73
  • 106
  • I struggled to understand how this was working but, on further investigation, supplying no top/left seems to be the key? – iamkeir Oct 23 '14 at 16:17
  • 1
    The key to understand the positioning: If you leave out a vertical (`top`/`bottom`) or horizontal (`left`/`right`) positioning value and using `position: absolute`, the element will be displayed where it would be without absolute position whilst still not affecting the layout of the outside elements. – yunzen Oct 27 '14 at 08:30
  • Ah, so defaults to 'auto'. In all my 11 years of web dev, I genuinely never knew this - thanks! – iamkeir Oct 27 '14 at 12:06
3

Elements with position: absolute take up the size of their children.

You can either set width: 100%; on the ul, or set left: 0; right: 0; which will also stretch it to the right size.

Also you might want to set list-style:none; on the nested ULs as well as top one.

kapa
  • 77,694
  • 21
  • 158
  • 175
  • Hi. Sorry but it doesn't work for me. The key problem is the fact that I want the nested LI to float left. They don't float because the UL is not large enough. It does take the space of the bigger LI, but NOT the total space occupied by all the LI. :( – Luca Reghellin Jan 16 '11 at 09:49
  • 4
    a test page will be needed then – kapa Jan 16 '11 at 10:10
3

To the person who suggested you use ids and classes, it is not necessary except for perhaps the very first ul being id'ed as "menu". I say this because what you're looking for is very rigid and structured.

This is what I understand you're trying to accomplish: Get a horizontal main menu that shows another horizontal sub-menu underneath when hovering over the main menu "links"

As shown here:

----------------------------------------------------------------------
Menu-Item 1    |    Menu Item 2 (hovered over)    |    Menu Item 3    
----------------------------------------------------------------------
               |    Sub Menu 1    |    Sub Menu 2    |    Sub Menu 3
               -------------------------------------------------------

Now I'm no one to tell you how to design your menus. But as you can see right now, I'm not sure even how it should work. Should the second menu be left-aligned to the left most point of the main menu item it's hovered over? Or Should the second menu be left-aligned to the whole menu, all the way to the left?

If it's the first, the last menu item will have too small a space to have links under (one or two max) and if it's the second version, then people will get frustrated that they keep "losing" the sub-menu when trying to access the links all the way to the left when hovering over the last menu item (in our example: Menu Item 3). Trust me, most users are not that adept with the mouse and trust me, the first version would just look bad.

Thus people use vertical sub-menus when the main menu is horizontal, but again, I'm not going to question your motives. I'm gonna try to answer your question in the best way I can which will end up being the second way, set up as such:

----------------------------------------------------------------------
Menu-Item 1    |    Menu Item 2 (hovered over)    |    Menu Item 3    
----------------------------------------------------------------------
Sub Menu 1    |    Sub Menu 2    |    Sub Menu 3    |      (blank)    
----------------------------------------------------------------------

Firstly, your code is not even set-up to have it go this way. The way your code is currently written, it'll end up like the first example I showed you (which as I explained will have very little horizontal space for the last menu item to show things, if your menu takes up the width of your page of course)

So to accomplish this goal you will have to get rid of the relative positioning of your li. Another idea if you MUST for some reason have it set to relative is to set each li of your main menu to a different z-index and to set them left margin/padding as well as right margin/padding as to "fit" your entire menu. I find this to be a "hack" and would rather omit the relative positioning of your li.

The absolute ul nested underneath your relative object (whatever it be, in this case it is the menu

  • ) can only take up as much space as you've designated. In this case 100% will only be the size of the li it is nested under. You could set it to something greater than 100%, such as 500px, but this would cause a problem for two reasons: 1) You will not be able to set all sub-menus to the absolute menu left as you probably want it to, becuase the absolute left of the nested element will only be the left most point of the li it is nested under. and 2) This is not good because the right most menu's sub-items will drift off to the right of the overall menu.

    If you must keep the main menu positioned relative, you will have to individually set the left position of each nested ul manually. And for all but the first one this will be a negative number and will not be an exact science as all browsers do not display the same font (and thus will cause slight changes in the width of the menu items, to counter this you'll need to use images for perfect width across all browsers of all ages). But this distracts from the beauty of CSS which is using few lines to design multiple elements.

    It would be much easier to remove the "relative" positioning off the main menu li (#main-menu > ul > li) and to add some bottom padding to it, else you'll never be able to hover over the sub-menu, it'll just disappear each time. If I had to guess at what you're doing and I was coding the CSS/HTML I would do it as such:

    HTML:

      <li>A
        <ul>
          <li>A1<li>
          <li>A2<li>
          <li>A3<li>
        </ul>
      </li>
    
      <li>B
        <ul>
          <li>B1<li>
          <li>B2<li>
          <li>B3<li>
        </ul>
      </li>
    
      <li>C
        <ul>
          <li>C1<li>
          <li>C2<li>
          <li>C3<li>
        </ul>
      </li>
    
    </ul>
    

    CSS:

    ul#main-menu{ list-style:none;}
    
    ul#main-menu > li {
      float: left;
      margin-left: 1em;
      padding-bottom: 3em; /*make this the line-height space underneat the main menu, plus the heigh of the secondary menu, plus the extra space you wanna give the user to not lose focus on the second menu*/
    } 
    ul#main-menu > li:first-child {margin-left: 0;}
    
    ul#main-menu > li > ul {
      display: none;
    }
    ul#main-menu > li:hover > ul {
      position: absolute;
      display: block;
      left: 0;
      list-style: none;
    }
    
    ul#main-menu > li:hover > ul > li,
    ul#main-menu > li > ul:hover > li {
      position: relative;
      float: left;
      margin-left: 1em;
    } 
    ul#main-menu > li:hover > ul > li:first-child,
    ul#main-menu > li > ul:hover > li:first-child {margin-left: 0px}
    

    I've tested this on my own browser and it seems to work like you want it to. By now you should know two other ways to accomplish this, but with varying results and more work than this version.

  • victoroux
    • 286
    • 2
    • 10
    • Also bazmegakapa was not incorrect. Setting the width of the absolute ul nested under the relative li to 100% will only take up as much space as the parent above it plus the li element beneath it. And then the li will also be offset. You can easily test this by setting the background of the nested li under the ul under the main-menu li to #eee or something. Test it on a page – victoroux Nov 20 '11 at 06:29
    • Also I'm not quite sure what you were trying to build. If you were going to make the main-menu
    • s so wide as to fit multiple
    • s underneath it then you're already on the right track. All you'd have to do is manually set the width of the ul underneath that li in pixels (which for some reason you've said you don't want to do)
    • – victoroux Nov 20 '11 at 06:31
  • 1
    Also Alexei Ivanov seems to have already solved this for you. Why did I write all this when you have a working solution? FML. – victoroux Nov 20 '11 at 06:36