3

It's possible that there are already solutions or examples for this, but I have been unable to find one.

I want to create a menu that has a varying number of menu items, each with different widths (depending on their content). If there are too many menu items I would want them to take up more than one rows.

My first solution was a simple float. It was fine, but at the end of the rows there is an uneven unoccupied space:

The right end of the menu looks ugly

Disregard that the "Text" on the items are the same width. I just suck at drawing. Imagine that the texts have varying length but the same padding around them.

With a tricky border (border only on the left side of items) I'm able to hide this a little, but the clickable area is still the same as the white parts of the picture above. A hover effect also unmasks the trick.


Then I have done a little research about text-align justify and found solutions like this: "text-align: justify;" inline-block elements properly?

For first glimpse this is just what I want. The elements are aligning properly, they fill the available space at first sight. But there is a fundamental flaw: they have gaps in between them:

There are gaps

This is expected since this treats my elements like words in a text. I have found no way to fix this.


Later I have started looking around in flexbox land. This looks promising flexbox seems to be the holy grail of css, but I haven't been able to wrap my head around it yet.


Basically what I want is this:

Perfect

The buttons fill up the available space, the menu is multiline (when there is too much elements for one line), the entire menu is clickable (with visible hover effect).

Is there a way to do this?

I'm hesitant to use javascript powered solutions (like calculating and adjusting widths of elements). But if there is a very clean solution for this, I might end up using it.


EDIT:

The menu here looks nice, but only if the menu is single line. This hacks the last element by adding it to a different list and by adding a lot of space with an invisible :after { content ". . ." } element.

Community
  • 1
  • 1
vinczemarton
  • 7,756
  • 6
  • 54
  • 86
  • comment out the spaces between your inline block elements – Pete Nov 06 '13 at 13:06
  • @Pete do you mean the inline-block justifying solution? That way it would not be justified. – vinczemarton Nov 06 '13 at 13:12
  • 1
    been playing with the flex thing and this works in chrome and IE10 but nothing else: jsfiddle.net/74AJD/6. I'd probably go with a js solution - it's your best bet whilst the flex box isn't fully supported – Pete Nov 06 '13 at 16:34
  • This would be so brilliant if it worked on all major browsers! – vinczemarton Nov 06 '13 at 16:42
  • @Pete The only problem with this is firefox is [missing multi line flexbox support](https://bugzilla.mozilla.org/show_bug.cgi?id=702508). Otherwise this is exactly what I want. – vinczemarton Nov 06 '13 at 17:31
  • @Pete If you made your fiddle into an answer I would gladly accept it. – vinczemarton Nov 09 '13 at 22:50

1 Answers1

3

If you use the following flexbox styles, it will achieve what you want in IE10 and chrome. However I don't think this is fully supported in all browsers yet.

HTML

<ul>
    <li>test test</li>
    <li>test</li>
    <li>test testtest</li>
    <li>testtest</li>
    <li>test</li>
    <li>test test test</li>
    <li>test test</li>
    <li>test</li>
</ul>

Css

ul {
    list-style:none; margin:0; padding:0; 
    display: -webkit-box;
    display: -moz-box;
    display: -ms-flexbox;
    display: -webkit-flex;
    display: flex;
    -webkit-flex-direction: row;
    -ms-flex-direction: row;
    -moz-flex-direction: row;
    flex-direction: row;
    -webkit-flex-wrap: wrap;
    -ms-flex-wrap: wrap;
    -moz-flex-wrap: wrap;
    flex-wrap: wrap;
    width: 300px; 
}

li {
     white-space:nowrap;  
     border:1px solid #cccccc; 
     padding:5px; 
    -webkit-flex: auto;
    -moz-flex: auto;
    -ms-flex: auto ;
    flex: auto;
}

Example


Edit by the question author:

I thought I should update this answer as I have learned a few things about flexbox.

  1. Dealing with multiline flexboxes, vendor prefixes are pretty much useless. It is really hard to find a version of any browser that supports multiline flexbox, but with a vendor prefix. Usually the vendor prefix refers to an older version of flexbox where multi lines are handled very differently.

  2. Every major browser supports this, except for Firefox, but an implementation is already merged into alpha, so it will surface in Firefox 28. Most of the desktop users will see your design (still it is important to implement a usable fallback).

  3. It is really easy to test multiline flexbox support with Modernizr as the Flexible Box Model test specifically tests for the flex-wrap property, which is only present in implementations supporting it. It is very clean to implement a float: left based fallback for every browser that doesn't.

My final implementation featuring a Modernizr based fallback goes like this:

ul {
    /* general */
    list-style: none;
    margin: 0;
    padding: 0;
    width: 100%;
}
.flexbox ul {
    display: flex;
    flex-flow: row wrap;
}
li {
    /* general */
    white-space: nowrap;
    border: 1px solid #555555;

    /* fallback */
    float: left;
}
.flexbox li {
    flex: auto;
}
li:first-child {
    /* general */
    text-align: right;
    order: 1;

    /* fallback */
    float: right !important;
}

It should be working in every browser, although the fallback implementation is not nearly as neat as the flexbox.

Also notice that li:first-child is the last element of the flexbox. That is because the order: 1 property. All other elements have a default order of 0. This is because it was easier to float it to the right in the fallback this way.

vinczemarton
  • 7,756
  • 6
  • 54
  • 86
Pete
  • 57,112
  • 28
  • 117
  • 166
  • `flex-wrap` works in opera, safari, chrome and IE. I can also check for flexbox support with modernizr, and implement a fallback design. I will possibly edit your answer to add more detail on the subject. – vinczemarton Nov 11 '13 at 11:39
  • cool, would be good to see how it is done for opera and safari – Pete Nov 11 '13 at 13:39