9

OK so this is actually a little complicated.

I have a navigation list where the list items are set to inline-block. The number of items is the list is dynamic so may vary.

My aim is to have the list items span the whole width of the container. (e.g. if there were 4 list items each one would take up 25% of the container width [ignoring margin/padding etc])

There is the added complication that browsers seem to add a 4px margin to inline-block elements where there is whitespace between them (linebreak/space etc).

I have made a fiddle as a starting point which has 2 examples: the first is just the list items in inline-block mode which the 2nd justifies them accross the width.

Neither achieves what I want which is for the whole width to be taken up by the elements without them breaking onto another line.

http://jsfiddle.net/4K4cU/2/

edit: slightly separate but why in my 2nd example is there a space beneath the lis, dispite the fact I have set line-height and font-size to 0?

harryg
  • 23,311
  • 45
  • 125
  • 198
  • Are you willing to set a fixed height to the list items? – Marc Audet Jun 12 '13 at 16:00
  • yes, yes I am. (convoluted reply due to min characters needed) – harryg Jun 12 '13 at 16:01
  • 2
    `display:table-cell` does the trick: http://stackoverflow.com/a/10526275/27862 – user123444555621 Jun 12 '13 at 16:23
  • think this does it but I need to put the -4px fix in for it to work: http://jsfiddle.net/4K4cU/7/ – harryg Jun 12 '13 at 16:45
  • Be aware that the -4px fix creates the same fundamental problem as removing whitespace between the `li` tags, but in reverse. The layout will be dependent on *always having* whitespace between the tags, rather than on *never having* it (both cases violate the separation of content and presentation, creating a risk of difficult-to-identify bugs if the formatting of the source code changes later on). It's [safer to float them](http://stackoverflow.com/questions/16465378/how-to-get-rid-of-unwanted-space-between-inline-block-columns/16469536#16469536) instead, if that's an option. – Matt Coughlin Jun 12 '13 at 18:56

5 Answers5

12

OK, despite many decent answers and my inital thinking that js/jquery was the only way to go there is in fact a good css-only solution: using table cells. Original suggestion by @Pumbaa80

.list {
    margin:0;
    padding: 0;
    list-style-type: none;
    display: table;
    table-layout: fixed;
    width:100%;
}
.list>li {
    display: table-cell;
    border:1px green solid;
    padding:5px;
    text-align: center;
}
.container {
    border: 1px #777 solid;
}
<div class="container">
    <ul class="list">
        <li>text</li>
        <li>text</li>
        <li>some longer text</li>
        <li>text</li>
    </ul>
</div>

This is superior to other solutions as:

  • css-only
  • no 4px margin problem as with inline-block
  • no clearfix need for floated elements
  • maintains equally distributed width independent of li content
  • concise css

Fiddle here: http://jsfiddle.net/rQhfC/

harryg
  • 23,311
  • 45
  • 125
  • 198
10

It's now 2016 and I wanted to update this question with an answer using flexbox. Consult with CanIUse for browser-compatiblity.

/* Important styles */
ul {
  display: flex;
}
li {
  flex: 1 1 100%;
  text-align: center;
}

/* Optional demo styles */
* {
  margin: 0;
  padding: 0;
}
ul {
  margin-top: 2em;
  justify-content: space-around;
  list-style: none;
  font-family: Verdana, sans-serif;
}
li {
  padding: 1em 0;
  align-items: center;
  background-color: cornflowerblue;
  color: #fff;
}
li:nth-child(even) {
  background-color: #9980FA;
}
<ul>
  <li>text</li>
  <li>text</li>
  <li>text</li>
  <li>text</li>
</ul>

Pre-edit fiddle (now inlined in above snippet)

kano
  • 5,626
  • 3
  • 33
  • 48
2

Here is one way of modifying your original concept.

The CSS is:

.list {
    padding:0;
    margin:0;
    list-style-type:0;
    overflow: hidden;
    height: 42px;
}
.list li {
    display: inline-block;
    line-height: 40px;
    padding: 0 5px;
    border:1px green solid;
    margin:0;
    text-align:center;
}

On your parent container, .list, set a height to enclose the child elements. In this case, I chose 40px and added 2px to account for the border.

Also, set overflow: hidden on .list to hide the 2nd line generated by the pseudo-element.

On the li elements, set line-height: 40px which will center the text vertically.

Since the height is fixed, the second line gets hidden and you can style your parent with a border and so on without extra white space breaking the design.

Demo: http://jsfiddle.net/audetwebdesign/WaRZT/

Not Foolproof...

In some cases, you may have more links than can fit on a single line. In that case, the items could force a second row to form and because of overflow hidden, you would not see them.

Evenly Spaced Border Boxes

If you want the border boxes to be evenly distributed, you need to set a width to the li elements.

If the content comes from a CMS, and you have some control over the coding, you can dynamically generate a class name to set the correct width using predefined CSS rules, for example:

.row-of-4 .list li { width: 24%; }
.row-of-5 .list li { width: 19%; }
.row-of-6 .list li { width: 16%; }

See: http://jsfiddle.net/audetwebdesign/WaRZT/3/

Marc Audet
  • 46,011
  • 11
  • 63
  • 83
  • thanks, that fixes the justified example but how to make the `li`s span the entire width? – harryg Jun 12 '13 at 16:18
  • Oooops... will the `li`'s have the same width or will that depend on the link text? – Marc Audet Jun 12 '13 at 16:20
  • Ideally they would be equally spaced over the width of the container. e.g. for 4 `li`s they would be 25%, 5 would be 20% etc. But without knowing how many `li`s will be there. I feel this may need to use a bit of jQuery. – harryg Jun 12 '13 at 16:22
  • So the border boxes should be 25% (for example) and the text within centered horizontally? For example: http://jsfiddle.net/audetwebdesign/WaRZT/2/ – Marc Audet Jun 12 '13 at 16:26
  • 1
    You have a few options, if the number of links is determined on the server side, you could generate a class name that gets appended to the parent container and that could be used to select the correct CSS rule. Otherwise, you need to use js to count the number of li items and then set the correct % value. See my new comment about the number of links. – Marc Audet Jun 12 '13 at 16:36
  • That is an idea. The links are generated from wordPress so might look into if there's a way to obtain number of top level menu items from the docs – harryg Jun 12 '13 at 16:38
1

There are multiple fixes to this. The one I prefer is simply to remove the whitespace between the elements, simply because the font-size trick involves non-semantic CSS. And its a lot easier haha. Code because answer requires it:

<ul class="list">
    <li>
        text
    </li><li>
        text
    </li><li>
        text
    </li><li>
        text
    </li>
</ul>

Updated jsFiddle, where the first list has items set to width:25%; and fits in the window on one line. If this isn't what you were going for, I must have misunderstood.

EDIT: for unknown number of list items

There is some CSS3 stuff for this, but to be cross-browser compatible back to IE8, you want a JS solution. Something like this should work:

var listItems =  document.querySelectorAll('li');
listItems.style.width = listItems.parentNode.style.width / listItems.length;

SECOND EDIT: for jQuery instead of JS

Winging it, but:

var $listitems = $('.list').children();
$listitems.width($listitems.parent().width()/$listitems.length);
PlantTheIdea
  • 16,061
  • 5
  • 35
  • 40
  • Not really the answer I was looking for. I am aware of the unwanted margin and how to fix it (well described [here](http://css-tricks.com/fighting-the-space-between-inline-block-elements/)), but it adds extra complication to the overall problem – harryg Jun 12 '13 at 16:13
  • Ah I see update. Yes I figured a bit of javascript/jQuery might be needed but was hoping to make it css-only – harryg Jun 12 '13 at 16:14
  • css3 columns require a static number, css3 calc also requires a static number ... for a truly dynamically generated list item width that will work for every current browser, JS is the way to go. with jQuery its easier to implement, i just wasnt making the assumption u used jQuery. – PlantTheIdea Jun 12 '13 at 16:20
  • yes, it looks like jQuery is the way to go. Guess I'll have to resort to it. – harryg Jun 12 '13 at 16:23
0

you can use the display:inline-block with li element,and use the text-align:justify with ul element. If you are interested ,please click here.

Airen
  • 2,139
  • 1
  • 14
  • 10