tags:

views:

1445

answers:

5

I have a horizontal menu. The markup looks like this:

<ul class="menu">
   <li>Item 1</li>
   <li>Longer Item 2</li>
   <li>Item 3</li>
</ul>

Submenus and suckerfish dropdowns will come later.

The ul needs to span the width of the page (e.g. 100% or 1000px).

The lis should vary in width based on their content.

So the result would look like this:

-----------------100% of page------------
|--Item 1--|--Longer item 2--|--Item 3--|

Now it is easy to do this by fixing a width for each li tag, but because the menu will be CMS driven I need to allow the width of the tabs to vary automatically. With a table this would be trivial, but I can't think of a way to do it with a ul.

A: 

If you are trying to style the ul (such that it stretches across the entire page), your best bet is to wrap it in a div, and style the div, and then allow the ul to just stretch as far as its content carries it

LorenVS
+3  A: 

This is a case for

Display:Table-Man

ul {
  display: table;
  width: 100%;
  table-layout: fixed;
}
li {
  display: table-cell;
}

Unfortunately, you should abandon the thought of supporting IEs 6 and 7, but else this is the way to go (or switching to HTML tables, which might or might not be so far away from the semantic content of the markup).

Boldewyn
+1 Hilarious!....
Justin Johnson
Thanks - of course I need at least IE 7 support :)
cbp
+3  A: 

If you're happy using JavaScript, jQuery could solve the issue as follows

var $menu, totalwidth;

$menu = $('ul.menu');
totalwidth = ($menu.width()/100);

$('ul.menu li').each(function(){
    this.css('width',String(Math.floor(this.width()/totalwidth))+'%');
});

$menu.css('width','100%');

That's untested, but looks right to me. Comment if you've any questions at all.

Gausie
The explicit string cast is unnecessary, but otherwise, you beat me to it ;)
Justin Johnson
This will probably end up being trickier than this: $menu.width() will be 100% ( though the actual value you get will be in pixels) by default unless you float the UL because UL is a block element. So you will have to float then unfloat the UL in order to get the right value for totalwidth. Or you could get totalwidth by summing the widths of the LI elements first and then going through them again to adjust to new width.
wheresrhys
That was my first thought, but I didn't want to do two loops...Floating and unfloating sounds like a safe bet though, should I edit my code?
Gausie
Unless floating and unfloating will be seen by the user...
Gausie
Thanks - yeah this is a starting point but it also has to be more complex to account for rounding errors. My answer is too ugly to post, sorry!
cbp
This also produces kind of an ugly result - the padding adjust proportionally to the width of the items. Really we want fixed sized paddings - I'll answer below.
cbp
+1  A: 

I reckon boldewyn's suggestion should work. I would use this approach for modern browsers and then use conditional comments to feed the following to ie6/7, so that the nav looks ok there , though won't span the 100% width.

ul {
  width: 100%;
}
li {
  float:left; // or display:inline-block;
}
wheresrhys
+1  A: 

Here's my jquery solution:

var actualWidth = 1000;
var totalLIWidth = 0;

// Calculate total width of list items
var lis = $('ul li');

lis.each(function(){
    totalLIWidth += $(this).width();
});

// Work out how much padding we need
var requiredPadding = Math.round(((actualWidth-totalLIWidth)/lis.length)/2);

// To account for rounding errors, the error is going to be forced into the first tab.
var roundingErrorFix = (requiredPadding*lis.length*2)+totalLIWidth-actualWidth;

// Apply padding to list items
var isFirst = true;
lis.each(function() {
    if(isFirst) {
     $(this).css('padding-left',requiredPadding-roundingErrorFix+'px')
      .css('padding-right',requiredPadding-roundingErrorFix+'px');
     isFirst = false;
    }
    else
     $(this).css('padding-left',requiredPadding+'px')
      .css('padding-right',requiredPadding+'px');
});
cbp