16

Let's say I have a menu of a pre-specified width.

I want each menu item to shrink-to-fit with its contents, with the exception of ONE, which then fills up the remaining space in the menu.

Like this:


                                                       Fill | item | item | item

So far the closest I've come to achieving this effect is by using display:table-cell in the css code for each menu item. But the problem is unless I define a width for the item's, they all expand to take up the same amount of width in the table.


      Fill      |      item      |      item      |      item      |      item

Is there any way to have the item spaces shrink to fit the item and have the fill item just fill up the rest of the div?

misterManSam
  • 24,303
  • 11
  • 69
  • 89
Vervious
  • 5,559
  • 3
  • 38
  • 57
  • So do you want to use a table or not? – MatTheCat Jan 03 '11 at 08:56
  • I don't want to use a table, but I want to achieve the above described effect, where one item fills up the space that isn't taken up. Fills up, meaning takes up all of it. – Vervious Jan 03 '11 at 17:51
  • It seems like you're not giving a very clear reason. Why do you **NEED** to fill the space? What's wrong with (eg) a set of floated divs in a parent? what's so important about the filling in? You'ld be surprised what can make the problem easier, when we know all about the problem. – jcolebrand Jan 04 '11 at 02:31
  • Oh. I was hoping for a visual effect (which is why it's not all that important that I get this) where I could hover over the "fill" item and have the rest of the menu light up, since the "fill" item is just special that way. (like a "links" menu item in a WordPress theme) – Vervious Jan 04 '11 at 02:36
  • possible duplicate of [How to make last cell of a row in a table occupy all remaining width](http://stackoverflow.com/questions/1060933/how-to-make-last-cell-of-a-row-in-a-table-occupy-all-remaining-width) – Ciro Santilli OurBigBook.com Oct 26 '14 at 20:02

6 Answers6

10

For those who want to do it with a table, you can give the first "td" a class and set it to a ridiculously large width, then it will push and force the other columns to shrink as much as it can. It works because tables can't expand more than the width to which they were set.

For example:

<!DOCTYPE html>
<html>
<head>
<style type="text/css">
table td {
    border:solid 1px #000;
}
table td.fill {
    width:9999em;
    text-align:right;
}
table {
    width:300px;
    border-collapse:collapse;
}
</style>
</head>
<body>
    <table>
        <tr><td class="fill">Fill</td><td>item</td><td>item</td><td>item</td></tr>
    </table>
</body>
</html>

In the old example that worked in Firefox and IE only I set the "td" width to 0, then it shrinked the column as much as it could.

You can set all columns to shrink-to-fit by setting the td width to something small, like 1px when you don't set the table width, this works in Chrome, Firefox and IE.

table td {
  width:1px;
}

You might want to set white-space:nowrap; too, otherwise all the words will wrap, or give the td some larger width

Timo Huovinen
  • 53,325
  • 33
  • 152
  • 143
  • 1
    I gave you +1 after I tested in IE. Then I realized, this was not supported in chrome. Haven't tested other browsers, but this is a major flaw. As of July 2012, Chrome accounts for 27% of browser usage, IE 23%, Firefox 19%, and Safari 15%. – Mr Jones Aug 24 '12 at 13:41
  • 2
    @MrJones thanks for you note Mr Jones, I don't remember if it worked in chrome when I made it, I might have only tested it in firefox and IE and sort-of expected Opera and Chrome to also work. **I fixed the example above** by reversing the logic, instead of setting all of the td's to have width 0, I set the width of .fill to be ridiculously large, now it works in Chrome, Opera, Firefox, IE5.5-9 – Timo Huovinen Aug 30 '12 at 08:03
  • 1
    +1 because the good old "ridiculously large width" trick works when given in `px` or `em` as you did here, but fails in Safari when given as `100%`. I lost days on this one. – Tobia Mar 13 '13 at 15:18
7

One way I've dealt with this issue is to apply width: 1px; to every <TH> where I'd like their cells to shrink. Then on each individual cell where I want to shrink to the content, I apply white-space: nowrap;. This prevents the cell from actually shrinking to 1px.

I'm currently using this method on a table of financial figures so I'm not sure if it works with longer paragraphs of text.

cjezowicz
  • 71
  • 1
  • 3
  • This method worked for me in Firefox 16.0.2, but in Chrome 23.0.1 it went back to the standard td width - the same width as it had without these CSS attributes. Good idea with the white-space attribute though. – Kevin Lawrence Nov 14 '12 at 09:27
  • Worked for me on Firefox and Chromium. Most people will probably use: `table tr > :not(:last-child) { white-space: nowrap; width: 1px;}` – Ciro Santilli OurBigBook.com Apr 08 '14 at 12:59
3

Why not use a float instead? Just reverse the order of your menu items.

#menubar {
  background-color: lime;
  width: 100%;
}
.menuitem {
  float: right;
  padding-right: 10px;
  padding-left: 10px;
}

/* Clear float */
#menubar:after {
  content: ' ';
  display: block;
  clear: right;
}
<div id="menubar">
  <div class="menuitem">item</div>
  <div class="menuitem">item</div>
  <div class="menuitem">item</div>
  <div class="menuitem">item</div>
  <div class="menuitem">fill</div>
</div>

Alternate way to use display: inline-block that fills the width:

#menubar {
  width: 100%;
  text-align: right;
}
.menuitem {
  display: inline-block;
  background-color: aqua;
}
.filled {
  width: 100%;
  background-color: gray;
}
<div id="menubar">
  <div class="menuitem filled">fill
    <div class="menuitem">item</div>
    <div class="menuitem">item</div>
    <div class="menuitem">item</div>
  </div>
</div>

The only downfall with this second method is that the menu item's are actually contained within the first menu item / filled. It does the trick for filling width:100%... If they are drop-down menus you will probably want to do some z-index work and collapse borders / set padding / etc...

misterManSam
  • 24,303
  • 11
  • 69
  • 89
CarpeNoctumDC
  • 1,760
  • 1
  • 12
  • 16
  • The problem is that menuitem "fill" doesn't exapand to take up the entirety of the rest menubar; it just floats right. – Vervious Jan 03 '11 at 17:57
  • Strange question: So you need the fill to truly fill the menubar? – CarpeNoctumDC Jan 04 '11 at 02:03
  • 1
    Try the second float method... (bottom half) ... if that doesnt work for you really the only cross browser/safe method left would be to use tabless... Why does the menu/fill "have" to "fill" the space? – CarpeNoctumDC Jan 04 '11 at 02:26
  • Part two is a reasonable workaround. I don't think there's going to be another way so I'm accepting your answer. thanks :) – Vervious Jan 04 '11 at 05:28
2

Welcome to the year 2014

Let's get flexible

We can now look forward to the future when Flexbox is standard. Currently a W3C Candidate Recommendation, Flexbox is currently limited by browser support (importantly, only IE 11 supports the current model).

Flexible navigation with a larger first-child

The markup

<nav>
    <a href="#">Fill</a>
    <a href="#">Item</a>
    <a href="#">Item</a>
    <a href="#">Item</a>
</nav>

The flex

The regular links get flex: 0 0 auto:

  • 0 - don't grow
  • 0 - don't shrink
  • auto - don't let the text overflow (auto is supposedly default, but text overflows without it set)

The first link gets flex: 1 0 auto:

  • 1 - take up the remaining width
  • 0 - don't shrink
  • auto - don't let the text overflow
nav {
  display: flex;
}
nav a {
  flex: 0 0 auto; 
}
nav a:first-child {
  flex: 1 0 auto;
}

This Flexbox guide is very helpful.

The combination (with added fluff)

Run the snippet below

/* 
For the purpose of this demo:
toggle active class on click
*/

$('nav a').on('click', function() {
  $('nav a').removeClass('active');
  $(this).toggleClass('active');
});
/* Foundation Properties */
nav {
  display: flex;
}
nav a {
  flex: 0 0 auto;
  text-align: center;
}
nav a:first-child {
  flex: 1 0 auto;
  text-align: right;
}

/* Fluff Properties */
body {
  background: #f5f5f5;
  margin: 0;
}
nav {
  max-width: 900px;
  margin: 0 auto;
  background: #3f51b5;
}
nav a {
  font-family: Helvetica;
  color: #c5cae9;
  font-size: 0.8em;
  text-transform: uppercase;
  text-decoration: none;
  border-bottom: solid 2px #3f51b5;
  padding: 4em 1em 0;
  transition: all 0.5s;
}
nav a.active {
  color: #fce4ec;
  border-bottom: solid 2px #fce4ec;
}
nav a:hover {
  color: #FFF;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<nav>
  <a href="#" class="active">Fill</a>
  <a href="#">Item</a>
  <a href="#">Item</a>
  <a href="#">Item</a>
</nav>
misterManSam
  • 24,303
  • 11
  • 69
  • 89
1

A solution using tables

<style type="text/css">

#menubar {
    width:100%;
}

.FillBar {
    width:100%;
    text-align:right;
    background-color:gray
}

</style>



<table id="menubar">
<tr>
<td class="FillBar">Fill</td>
<td>item</td>
<td>item</td>
<td>item</td>
<td>item</td>
<td>item</td>
</tr>
</table>

Just add style (padding/spacingp/etc) or whatever else you need...

CarpeNoctumDC
  • 1,760
  • 1
  • 12
  • 16
0

Similar to what @cjezowicz is talking about, you can use white-space together with making one cell width: 100% to achieve the effect you're looking for:

<div id="row">
   <div class="cell fill">Fill</div>
   <div class="cell shrink">Item</div>
   <div class="cell shrink">Item</div>
   <div class="cell shrink">Item</div>
   <div class="cell shrink">Item</div>
</div>

<style type="text/css">

   div#row { display: table; width: 100%; }

   div.cell { display: table-cell; text-align: center; vertical-align: middle; }

   div.cell.fill { width: 100%; }

   div.cell.shrink { white-space: pre; }

</style>

Then, if you need to force a set width for any of the shrunken cells, use the min-width on that cell.

atwixtor
  • 795
  • 10
  • 26