25

I'd like to have a table with headers down the left hand side, the headers should be as narrow as possible, and the space for the values should take up the rest of the width of the page. The headers should all be on a single line, unless the content of the header cell is very long, at which point the header should wrap onto multiple lines.

With short content, this works:

<table border="1">
    <tr>
        <th>Short Header</th>
        <td>value</td>
    </tr>
    <tr>
        <th>Quite Long Header</th>
        <td>value</td>
    </tr>
</table>

CSS:

table {
    width: 100%;
}

th {
    text-align: right;
    width: 10px;
    white-space: nowrap;
}

However, when I try to add a maximum width, instead of wrapping, the content spills out of the text box:

table {
    width: 100%;
}

th {
    text-align: right;
    width: 10px;
    white-space: nowrap;
    max-width: 300px;
    word-break: break-all;
}

Demo: https://jsfiddle.net/2avm4a6n/

It seems that the white-space: nowrap; style overrides the word-break: break-all;, so the max-width: 300px; causes the text to spill out. I'm using the white-space: nowrap; and width: 10px; combination to make the header column as narrow as possible, without wrapping headers with spaces in below the minimum width, but having just the width: 10px; and the word-break: break-all; causes the headers to wrap into single characters to fit in the narrow 10px.

Any idea how to do this with css?

Douglas
  • 36,802
  • 9
  • 76
  • 89
  • Does it have to still remain `display: table` ? Or can it vary from it's appearance (of course still with borders etc) – chrona Jul 07 '15 at 13:51
  • @chrona: I've got lots of rows, and I'd like all the elements in the right hand column to still be left aligned with each other horizontally regardless of the content in the left hand column, if that's possible without using a table that's fine. – Douglas Jul 07 '15 at 14:18

6 Answers6

20

Remove the table width, from th remove the width, the white-space, and the word-break. Add to the th word-wrap: word-break

I also suggest you to set min-width: 150px or any value you want. Also add this td { width: 100%;}.

Here is the jsFiddle

th {
    text-align: right;
    max-width: 300px;
    min-width: 150px;
    word-wrap: break-word;
}
td {
    width: 100%;
}
<table border="1">
    <tr>
        <th>Short Header</th>
        <td>value</td>
    </tr>
</table>
<br />
<table border="1">
    <tr>
        <th>Short Header</th>
        <td>value</td>
    </tr>
    <tr>
        <th>Quite Long Header</th>
        <td>value</td>
    </tr>
</table>
<br />
<table border="1">
    <tr>
        <th>Short Header</th>
        <td>value</td>
    </tr>
    <tr>
        <th>Quite Long Header</th>
        <td>value</td>
    </tr>
    <tr>
        <th>MMMMMMMMMMMMMMMMMMMMMMMMMMMassiveHeader</th>
        <td>value</td>
    </tr>
</table>
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Almis
  • 3,684
  • 2
  • 28
  • 58
  • FYI - it doesn't make the TH to receive the minimum width in the 3rd table. – Stickers Jul 10 '15 at 13:15
  • 2
    Note that if none of the header words run over the max-width (that is, if a long header is "a really really really long header" not "areallyreallyreallylongheader") you can leave out the `word-wrap` and just use the `max-width`, `min-width`, and the `td` `width` – henry Jul 13 '15 at 17:06
9

Let me know if this is what you are looking for:

remove white-space:nowrap and word-break:break-all and add word-wrap:break-word

see snippet below:

table {
    width: 100%;
}

th {
    text-align: right;
    width: 10px;
    max-width: 300px;
    word-wrap:break-word    
}
<table border="1">
    <tr>
        <th>Short Header</th>
        <td>value</td>
    </tr>
</table>
<br />
<table border="1">
    <tr>
        <th>Short Header</th>
        <td>value</td>
    </tr>
    <tr>
        <th>Quite Long Header</th>
        <td>value</td>
    </tr>
</table>
<br />
<table border="1">
    <tr>
        <th>Short Header</th>
        <td>value</td>
    </tr>
    <tr>
        <th>Quite Long Header</th>
        <td>value</td>
    </tr>
    <tr>
        <th>MMMMMMMMMMMMMMMMMMMMMMMMMMMassiveHeader</th>
        <td>value</td>
    </tr>
</table>

UPDATE: OP Comment - may 18th

Thanks, this is close, but it shouldn't wrap the short headers: it should only start wrapping once the header column reaches the max-width

Add a min-width to th that fits you the best. (Note that th which will break words don't receive the min-width) as pointed out by @Pangloss in a comment to another answer.

see snippet below:

table {
    width: 100%;
}

th {
    text-align: right;
    width: 10px;
    min-width:133px; /* change the value for whatever fits you better */
    max-width: 300px;
    word-wrap:break-word    
}
<table border="1">
    <tr>
        <th>Short Header</th>
        <td>value</td>
    </tr>
</table>
<br />
<table border="1">
    <tr>
        <th>Short Header</th>
        <td>value</td>
    </tr>
    <tr>
        <th>Quite Long Header</th>
        <td>value</td>
    </tr>
</table>
<br />
<table border="1">
    <tr>
        <th>Short Header</th>
        <td>value</td>
    </tr>
    <tr>
        <th>Quite Long Header</th>
        <td>value</td>
    </tr>
    <tr>
        <th>MMMMMMMMMMMMMMMMMMMMMMMMMMMassiveHeader</th>
        <td>value</td>
    </tr>
</table>
Community
  • 1
  • 1
dippas
  • 58,591
  • 15
  • 114
  • 126
6

If you know what the data is in the <th> already, you could then wrap the long length text into a <span> tag or so, and adjust the CSS slightly for that.

UPDATED JSFIDDLE

table {
    width: 100%;
}
th {
    text-align: right;
    white-space: nowrap;
}
th span {
    white-space: normal;
    word-break: break-all;
    display: block;
}
td {
    width: 100%;
}
<table border="1">
    <tr>
        <th>Short Header</th>
        <td>value</td>
    </tr>
</table>
<br />
<table border="1">
    <tr>
        <th>Short Header</th>
        <td>value</td>
    </tr>
    <tr>
        <th>Quite Long Header</th>
        <td>value</td>
    </tr>
</table>
<br />
<table border="1">
    <tr>
        <th>Short Header</th>
        <td>value</td>
    </tr>
    <tr>
        <th>Quite Long Header</th>
        <td>value</td>
    </tr>
    <tr>
        <th><span>MMMMMMMMMMMMMMMMMMMMMMMMMMMassiveHeader</span></th>
        <td>value</td>
    </tr>
</table>
Stickers
  • 75,527
  • 23
  • 147
  • 186
4

How about some CSS3 grid layout:

.grid {
    display: grid;    
    grid-template-columns: minmax(1fr, auto) auto;
}

.label {
    grid-column: 1;
    word-break: break-all;
    max-width: 300px;
}

.value {
    grid-column: 2;
}
<div class="grid">
    <div class="label">Short Header</div>
    <div class="value">value</div>
</div>
<br/>
<div class="grid">
    <div class="label">Short Header</div>
    <div class="value">value</div>
    <div class="label">Quite Long Header</div>
    <div class="value">value</div>
    <div class="label">Short Header</div>
    <div class="value">value</div>
</div>
<br/>
<div class="grid">
    <div class="label">Short Header</div>
    <div class="value">value</div>
    <div class="label">Quite Long Header</div>
    <div class="value">value</div>
    <div class="label">MMMMMMMMMMMMMMMMMMMMMMMMMMMassiveHeader</div>
    <div class="value">value</div>
    <div class="label">Quite Long Header</div>
    <div class="value">value</div>
</div>

To see this in action, you'll have to use Chrome, go to chrome://flags and enable the Enable experimental Web Platform features option.

Obviously this isn't much use at the moment but certainly something to look forward to!

Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
3

@Douglas :I think you done all things properly. but as in your given example and in question you said you want narrow header so you given 10px width to th. but if you want to achive 10px width in your example then remove only white-space: nowrap;

Then it loook like Example 1.

Example 1: https://jsfiddle.net/2avm4a6n/20/

Example 2: As all user given answer you can do that way also

Example 3: there is another option for you if you want to use white-space: nowrap; then apply one more thing i.e text-overflow: ellipsis; overflow: hidden; https://jsfiddle.net/2avm4a6n/21/

 text-overflow: ellipsis;
 overflow: hidden;

EDIT

And use text-align: left; if you want text to be start from left side

Keval Bhatt
  • 6,224
  • 2
  • 24
  • 40
  • Your "Example 2" doesn't seem very useful - perhaps you should remove it. For the other ones, maybe you could use a code snippet in the question, rather than linking to jsfiddle. – Tom Fenech Jul 10 '15 at 12:01
0

You can achieve this by giving a width to the "th" and make the text-align:left

html

 <table border="1">
        <tr>
            <th>Short Header</th>
            <td>value</td>
        </tr>
    </table>
    <br />
    <table border="1">
        <tr>
            <th>Short Header</th>
            <td>value</td>
        </tr>
        <tr>
            <th>Quite Long Header</th>
            <td>value</td>
        </tr>
    </table>
    <br />
    <table border="1">
        <tr>
            <th>Short Header</th>
            <td>value</td>
        </tr>
        <tr>
            <th>Quite Long Header</th>
            <td>value</td>
        </tr>
        <tr>
            <th>MMMMMMMMMMMMMMMMMMMMMMMMMMMassiveHeader</th>
            <td>value</td>
        </tr>
    </table>

css

table {
    width: 100%;
}

th {
    text-align: left;
    width: 500px;
}

Working fiddle: https://jsfiddle.net/2avm4a6n/16/

  • Unfortunately that makes the width of the first column in all of the tables at least 500px wide, in the first two tables where the headers are short, the first column should be narrower than in the last table. – Douglas May 18 '15 at 13:32