70

This is a hypothetical example:

table, thead, tbody, tr { width: 100%; }
    table { table-layout: fixed }
    table > thead > tr > th { width: auto; }
<table>
      <thead>
        <tr>
          <th>Column A</th>
          <th>Column B</th>
          <th>Column C</th>
          <th class="absorbing-column">Column D</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Data A.1 lorem</td>
          <td>Data B.1 ip</td>
          <td>Data C.1 sum l</td>
          <td>Data D.1</td>
        </tr>
        <tr>
          <td>Data A.2 ipsum</td>
          <td>Data B.2 lorem</td>
          <td>Data C.2 some data</td>
          <td>Data D.2 a long line of text that is long</td>
        </tr>
        <tr>
          <td>Data A.3</td>
          <td>Data B.3</td>
          <td>Data C.3</td>
          <td>Data D.3</td>
        </tr>
      </tbody>
    </table>

I want to have every single column's width to fit its content size, and leave the rest of the space for the one column with the "absorbing-column" class, so that it looks like this:

| HTML                                                                   | 100%
| body                                                                   | 100%
| table                                                                  | 100%
|------------------------------------------------------------------------|
| Column A | Column B       | Column C | Column D                        |
|------------------------------------------------------------------------|
| Column A | Column B lorem | Column C | Column D                        |
| Column A | Column B       | Column C | Column D                        |
| Column A | Column B       | Column C | Column D                        |
|------------------------------------------------------------------------|

You see, Column B is a bit bigger than the rest due to the extra data in the first row, but Column D always uses up the remaining space.

I played around with max-width, min-width, auto, etc. and could not figure out how to make this work.

In other words, I want all columns to take whatever width they need and not more, and then I want Column D to use up all of the remaining space inside the 100% width table.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
user2985898
  • 1,173
  • 1
  • 8
  • 21

4 Answers4

86

Define width of .absorbing-column

Set table-layout to auto and define an extreme width on .absorbing-column.

Here I have set the width to 100% because it ensures that this column will take the maximum amount of space allowed, while the columns with no defined width will reduce to fit their content and no further.

This is one of the quirky benefits of how tables behave. The table-layout: auto algorithm is mathematically forgiving.

You may even choose to define a min-width on all td elements to prevent them from becoming too narrow and the table will behave nicely.

table {
    table-layout: auto;
    border-collapse: collapse;
    width: 100%;
}
table td {
    border: 1px solid #ccc;
}
table .absorbing-column {
    width: 100%;
}
<table>
  <thead>
    <tr>
      <th>Column A</th>
      <th>Column B</th>
      <th>Column C</th>
      <th class="absorbing-column">Column D</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Data A.1 lorem</td>
      <td>Data B.1 ip</td>
      <td>Data C.1 sum l</td>
      <td>Data D.1</td>
    </tr>
    <tr>
      <td>Data A.2 ipsum</td>
      <td>Data B.2 lorem</td>
      <td>Data C.2 some data</td>
      <td>Data D.2 a long line of text that is long</td>
    </tr>
    <tr>
      <td>Data A.3</td>
      <td>Data B.3</td>
      <td>Data C.3</td>
      <td>Data D.3</td>
    </tr>
  </tbody>
</table>
gfullam
  • 11,531
  • 5
  • 50
  • 64
  • The "absorbing-column" class doesn't apply to "th" however? – NaturalBornCamper Mar 07 '17 at 07:13
  • 1
    @NaturalBornCamper Good catch. I've updated my example to apply the class only to `th` like the OP's snippet and I've modified the CSS to match. But the effect is the same because, as I point out, the table-layout algorithm is forgiving. The width applied to even one cell in the column, if not overridden by a rule of greater specificity, will result in the width being applied to all cells in the column. If different widths are applied to separate cells by two or more rules of the same specificity, the greatest width is used to layout that column (this seems to be true in Chrome anyway). – gfullam Mar 07 '17 at 15:06
  • 1
    In my experiments, this answer does not prevent the first columns to have content wrapped. The @Vitorino Fernandes answer works better in my opinion. – fred Jul 30 '20 at 09:49
  • That's a good observation. I didn't include the `white-space: nowrap` rule in my answer because it can result in the table overflowing its container, which is undesirable, but I should have directly explained that. I did mention that `min-width` can be used on the `td` elements, which can achieve a similar effect, but it didn't directly address the OPs requirements. I also like @Vitorino fernandes answer. Though, it too may be improved by resetting `white-space: normal` on `table td:last-child` to help avoid container overflow. – gfullam Jul 30 '20 at 15:38
  • 1
    table-layout: auto worked in my case actually – JaktensTid Dec 03 '20 at 13:02
43

demo - http://jsfiddle.net/victor_007/ywevz8ra/

added border for better view (testing)

more info about white-space

table{
    width:100%;
}
table td{
    white-space: nowrap;  /** added **/
}
table td:last-child{
    width:100%;
}

    table {
      width: 100%;
    }
    table td {
      white-space: nowrap;
    }
    table td:last-child {
      width: 100%;
    }
<table border="1">
  <thead>
    <tr>
      <th>Column A</th>
      <th>Column B</th>
      <th>Column C</th>
      <th class="absorbing-column">Column D</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Data A.1 lorem</td>
      <td>Data B.1 ip</td>
      <td>Data C.1 sum l</td>
      <td>Data D.1</td>
    </tr>
    <tr>
      <td>Data A.2 ipsum</td>
      <td>Data B.2 lorem</td>
      <td>Data C.2 some data</td>
      <td>Data D.2 a long line of text that is long</td>
    </tr>
    <tr>
      <td>Data A.3</td>
      <td>Data B.3</td>
      <td>Data C.3</td>
      <td>Data D.3</td>
    </tr>
  </tbody>
</table>
Vitorino fernandes
  • 15,794
  • 3
  • 20
  • 39
  • Would this work if the "absorbing column" is not at the end (of course, I mean instead of using :last-child, can I use a class and it affects any column the same)? – user2985898 Nov 19 '14 at 12:49
  • yes but you will need to add `.absorbing column` for all the last child [fiddle](http://jsfiddle.net/victor_007/ywevz8ra/1/) @user2985898 – Vitorino fernandes Nov 19 '14 at 13:01
  • @user2985898 - Looks like you can use `nth-child` to specify the column: http://jsfiddle.net/5ht73hpc/ – SwDevMan81 Feb 29 '16 at 22:01
  • 5
    If the text is wider that the cell, it will make the table wider than its container – DanV Jan 21 '18 at 22:05
  • @DanV To solve the table wider than its container, simply add 'white-space: initial;' to the 'td:last-child'. – fred Jul 30 '20 at 09:46
  • This solution worked for me as presented. – Ken Ingram Jan 18 '23 at 02:43
1

Setting CSS width to 1% or 100% of an element according to all specs I could find out is related to the parent. Although Blink Rendering Engine (Chrome) and Gecko (Firefox) at the moment of writing seems to handle that 1% or 100% (make a columns shrink or a column to fill available space) well, it is not guaranteed according to all CSS specifications I could find to render it properly.

One option is to replace table with CSS4 flex divs:

https://css-tricks.com/snippets/css/a-guide-to-flexbox/

That works in new browsers i.e. IE11+ see table at the bottom of the article.

Mladen Adamovic
  • 3,071
  • 2
  • 29
  • 44
-5

use overflow:

overflow: visible;
Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135