Search code examples
csslayoutoverflowtablelayoutcentering

Stretch Elements Into Unknown Width Between Two Fixed-Width Elements


Preface: I'm not generally a UI guy, so forgive me if I have a gross misunderstanding of CSS. Please know that this question is following several hours of research/banging my head against a wall :)

Essentially what I'm trying to do is stretch an unknown number of elements into a space of unknown width, between two elements of a known width, all inside a container that is of variable width, centered inside the page.

I realized how crazy that all sounded as I was typing it, so let me try doing this graphically:

html
|-------------------------------------------------------------------------|
|   #container                                                            |
|   |-----------------------------------------------------------------|   |
|   |#header                                                          |   |
|   ||---------------------------------------------------------------||   |
|   || #logo   | #menu                                     | #social ||   |
|   ||         | |---|  |---|  |---|                 |---| |         ||   |
|   ||         | | A |  | B |  | C |  ...  ...  ...  | N | |         ||   |
|   ||         | |---|  |---|  |---|                 |---| |         ||   |
|   ||---------------------------------------------------------------||   |
|   |                                                                 |   |
|   |#body                                                            |   |
|   ||---------------------------------------------------------------||   |
|   ||                                                               ||   |
|   ||---------------------------------------------------------------||   |
|-------------------------------------------------------------------------|

where:

  • #container is centered, and takes up 80% of the width of the document
  • #logo, #menu, and #social are each contained in <div/>s
  • #logo and #social have known widths; #menu does not
  • | A |...| N | are an unknown number of <li/> elements within a <ul/>

The HTML looks like:

<body>
    <div id="container">
        <div id="header">
            <div id="logo">Logo here</div>

            <div id="nav">
                <ul>
                    <li><a href="#">Home</a></li>
                    <li><a href="#">Home</a></li>
                    <li><a href="#">Home</a></li>
                </ul>
            </div>

            <div id="social"><a href="#">Link</a><a href="#">Link</a></div>

        </div>
        <div id="body">&nbsp;</div>
    </div>
</body>

Try as I might, I cannot get the A..N menu items to stretch across the remainder of the space between the #logo and the #social elements. They either end up smooshed together, or they take up 100% of the 80% allotted to them by the #container.

Some things I've already tried:

  • Using display: inline-table; (fiddle)
  • Displaying <li/> elements inline, and giving the #menu element a width of 100% (fiddle)
  • A bunch of variations on these

Things I can't/won't consider doing:

  • Housing the menu items in a <table/>
  • Any type of JavaScript approach
  • Using the CSS calc() function

And one last note: I'm not concerned about these menu items not having enough room in the space allocated to them. However, if they do overflow, I would expect them to spill over to another line inside the #menu (i.e. below the A menu item).

There has to be a pure CSS solution to this... What is it?


Solution

  • You are right to not want to use a <table> element, as that it not semantic.. but that is why CSS has a set of properties built to get elements to look like table elements, while retaining their semantic meaning.

    Basically, if you set a container element to display: table;, you can then set child elements to either display: table-row; or display: table-cell;

    What does this mean for you?

    Well, a set of table cells will always fill to 100% width of the parent, as well as 100% height of the parent, which is usually determined by the largest child, unless explicitly set.

    So, with this in mind, if you were to set your header element #header to display: table;, you can then set #logo, #nav, and #social to display: table-cell;. Setting fixed widths on the logo and social blocks will be respected, and therefore cause the nav block to fill the remaining space.

    The unordered list ul within the nav block can act as a second container.. once again being set to display: table;, allowing it's contents to fill to the full width of the nav block. Simply set the li elements to be display: table-cell; as well, and you should have the desired effect.

    See this demo:

    http://jsfiddle.net/5RvCC/

    • A nice bonus of table-cell styled elements is that they allow the vertical-align property, which makes vertical centring super easy, which always seems to be a common question.