Marty Wallace Marty Wallace - 1 month ago 8
HTML Question

Horizontal list items - fit to 100% with even spacing

I have a simple list and i am trying to get the list items to be evenly spaced horizontally, but still fill 100% of the width of the container regardless of the width of the container.

I do not want each list item to be of equal width, but instead the spacing between each list item to be even:

jsfiddle: http://jsfiddle.net/b6muX/1/

Also the number of list items might be dynamic and not the same as the number in my example.

Can this be done without js?

Here is my markup and css:

<ul>
<li>This is menu item 1</li>
<li>Number 2</li>
<li>Item Number 3</li>
<li>Menu 4</li>
</ul>


and the following css:

ul {
width: 100%;
background: #cacaca;
margin: 0;
padding: 0;
}
li {
list-style-type: none;
display: inline-block;
padding-right: 10%;
width: auto;
margin-right: 0.5%;
background: #fafafa;
padding-left: 0;
margin-left: 0;
}
li:last-child {
margin-right: 0;
padding-right: 0;
}

Answer

The new CSS flexbox specification would be the solution to your problem :)

ul {
    display: flex;
    align-items: stretch; /* Default */
    justify-content: space-between;
    width: 100%;
    background: #cacaca;
    margin: 0;
    padding: 0;
}
li {
    display: block;
    flex: 0 1 auto; /* Default */
    list-style-type: none;
    background: #fafafa;
}
<ul>
    <li>This is menu item 1</li>
    <li>Number 2</li>
    <li>Item Number 3</li>
    <li>Menu 4</li>
</ul>

See also: http://jsfiddle.net/teddyrised/b6muX/3/

If you would allow me to indulge myself in a bit of explanation:

  1. display: flex on the parent tells the parent to adopt the CSS flexbox model
  2. align-items: stretch tells the parent that its children should stretch to the full height of the row. This is the default value of the property.
  3. justify-content: space-between is the magic here — it instructs the parent to distribute remaining space left after laying out the children between them.

On the children:

  1. flex: 0 1 auto on the children tells them that:
    • flex-grow: 0: Do no grow, otherwise they will fill up the parent
    • flex-shrink: 1: Shrink when necessary, to prevent overflowing (you can turn this off by setting to 0.
    • flex-basis: auto: Widths of children element are calculated automatically based on their content. If you want fixed, equal width children element, simply set it to 100%.

You can adjust the padding to the <li> element as of when you see please.


Old CSS method: text-align: justify

The old method, while working perfectly, is a little more cumbersome as it requires you to reset the font-size in the unordered list element to eliminate spacing between child elements. It also requires you to render an pseudo-element to ensure that the content overflows the first row for text justification to kick in (remember that the default behavior of justified text is that a row that is not 100% will not be justified).

ul {
    font-size: 0; /* Eliminate spacing between inline block elements */
    text-align: justify;
    width: 100%;
    background: #cacaca;
    list-style: none;
    margin: 0;
    padding: 0;
}
ul:after {
    content: 'abc';
    display: inline-block;
    width: 100%;
    height: 0;
}
li {
    display: inline-block;
    background: #fafafa;
    font-size: 1rem; /* Reuse root element's font size */
}
p {
    font-size: 1rem;
}
<ul>
    <li>This is menu item 1</li>
    <li>Number 2</li>
    <li>Item Number 3</li>
    <li>Menu 4</li>
</ul>

See also: http://jsfiddle.net/teddyrised/b6muX/5/