Prevent text from wrapping in table until "max-width" is reached

Viewed 976

I use Java to create an HTML file (regular text file with .html extension) with a table and also fill it at runtime. Each column should be around 300px wide max (for now) but, other than that, the text should use all the space it needs: A column with only short text, like "123", in all cells should be quite narrow, while a column with a 200 character text should span the whole 300px and wrap the text into multiple lines.

The text is passed by a different Java class that I have no control over, so I don't know in advance what text and how much of it is going to be in the table. There could be 3 columns, or 30, and it's fine if the browser's horizontal scrollbar is shown.

I'm currently experiencing problems with short text:

If it contains white space characters (like a space " "), then the column's width is decreased and the text wraps into the next line, once the table is wider than the available screen width and the horizontal scrollbar is shown. If the cell doesn't contain any white spaces, then its width doesn't change.

I know about white-space: nowrap; but with that long text bleeds into the next cell, instead of wrapping at 300px.

If I use e.g. min-width: 100px, only the text that exceeds 100px is wrapped but then columns with little to no text are also 100px wide, even though they could be using up less space.

Question: How do I prevent short text that contains white space characters from wrapping until it hits the column's max-width mark (without overflowing/truncating)? I'm aware I could (probably) check the text's length in Java and change the cell's CSS style to either one that uses white-space: normal or another one with white-space: nowrap, once it exceeds a certain character count, but I'm interested in a solution that only uses vanilla HTML and CSS (is this even possible?).

You can change the width of the right side of the screen in this jsfiddle to see the problem.

Here's my code:

table {
  border: 1px solid black;
  border-collapse: collapse;
}

table td,
table th {
  border: 1px solid black;
  word-wrap: break-word;
  white-space: normal;
  /* min-width: 100px; */
  max-width: 300px;
}
<table>
  <tr>
    <th>Col1</th>
    <th>Col2</th>
    <th>Col3</th>
    <th>Col4</th>
  </tr>
  <tr>
    <td>A</td>
    <td>This is a long text!</td>
    <td>short</td>
    <td>12345678901234567890</td>
  </tr>
  <tr>
    <td>short</td>
    <td>short</td>
    <td>longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong</td>
    <td>11111111111111111111</td>
  </tr>
  <tr>
    <td>This</td>
    <td>is</td>
    <td>text!</td>
    <td>09876543210987654321</td>
  </tr>
</table>

2 Answers

You can almost achieve that by wrapping your table inside a container that has a large enough width.

For demonstration purposes, I've wrapped the whole html inside a <div> with class .page, limited width and scrolling overflow to emulate limited space on a page.

I'm then wrapping the entire table inside a <div class="container"> that has a width of 1000px. This allows for the table to size its columns according to their max-width and contents only.

Trouble is: it makes a very large scroll for a small table that actually barely overflows... This could be solved using javascript though (fix the width of .container according to table actual width).

.page { /* not required: emulates page flow with limited width */
  max-width: 500px;
  height: 90vh;
  overflow-x: scroll;
  background: gainsboro;
  padding: 5px;
}

.container {
  width: 1000px;
}

table {
  border: 1px solid black;
  border-collapse: collapse;
}

table td,
table th {
  border: 1px solid black;
  word-wrap: break-word;
  white-space: normal;
  /* min-width: 100px; */
  max-width: 300px;
}
<div class="page"> <!-- not required: emulates page flow with limited width -->
  <div class="container">
    <table>
      <tr>
        <th>Col1</th>
        <th>Col2</th>
        <th>Col3</th>
        <th>Col4</th>
      </tr>
      <tr>
        <td>A</td>
        <td>This is a long text!</td>
        <td>short</td>
        <td>12345678901234567890</td>
      </tr>
      <tr>
        <td>short</td>
        <td>short</td>
        <td>longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong</td>
        <td>11111111111111111111</td>
      </tr>
      <tr>
        <td>This</td>
        <td>is</td>
        <td>text!</td>
        <td>09876543210987654321</td>
      </tr>
    </table>
  </div>
</div>

I haven't found a way to set the widths (in a way that whitespaces don't mess with it) automatically yet - at least without an additional container that I can't know the width of in advance - that's why I'm using Java to assign one of 3 css styles:

if(text.length()>40) {
    if(text.length()>300) {
        stringBuilder.append("<td class=\"long2\">");
    } else {
        stringBuilder.append("<td class=\"long1\">");
    }
} else {
    stringBuilder.append("<td class=\"short\">");
}

Test HTML/CSS styles (created by hand, jsfiddle here):

table {
  border: 1px solid black;
  border-collapse: collapse;
}

table th {
  border: 1px solid black;
  word-wrap: break-word;
  text-align: center;
}

table td {
  border: 1px solid black;
  word-wrap: break-word;
  max-width: 300px;
  white-space: normal;
  vertical-align: top;
  text-align: left;
}

table td.short {
  white-space: nowrap;
}

table td.long1 {
  min-width: 300px;
}

table td.long2 {
  min-width: 500px;
  max-width: 500px;
}
<table>
  <tr>
    <th>Col1</th>
    <th>Col2</th>
    <th>Col3</th>
    <th>Col4</th>
    <th>Col5</th>
    <th>Col6</th>
  </tr>
  <tr>
    <td>A</td>
    <td class="short">This is a long text!</td>
    <td class="long1">short</td>
    <td class="long2">This is an even longer text!</td>
    <td>12345678901234567890</td>
    <td>12</td>
  </tr>
  <tr>
    <td>short</td>
    <td class="short">short</td>
    <td class="long1">longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong</td>
    <td class="long2">long long long long long long long long long long long long long long long long long long long long long long long long long long long long long</td>
    <td>11111111111111111111</td>
    <td>345</td>
  </tr>
  <tr>
    <td>This</td>
    <td class="short">is</td>
    <td class="long1">text!</td>
    <td class="long2">Something something something!!!</td>
    <td>09876543210987654321</td>
    <td>0</td>
  </tr>
</table>

Short explanation:

  • .short: <300px wide and uses no-wrap because there won't be more than a single line anyway. With the default font and font size it can fit around 45 regular characters without over-flowing (only tested with the Roman alphabet!) but this would have to be changed with other font settings/content, of course.
  • .long1: Exactly 300px wide. Without this break-word would make the column match the width of the longest word if the text contains any whitespaces.
  • .long2: Exactly 500px wide. If I didn't also set min-width the column would only be as wide as the longest word if the text contains any whitespaces. Basically: With whitespaces min-width sets the column size, without max-width is the important bit.
Related