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

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>

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

Making table row height not changing, when increasing the whole table height

When I increase the table height, all the rows get resized and the additional height is distributed equally. among them.
Question
Is it possible to make a row (in my example the one with headers) always stay at it's minimum height? As an analogy I see it as specifying flex-grow: 0 on a Flex item.
No fixed height
I don't want to make that row fixed height (e.g. set on it height: <fixed value in px>), just make it's height the natural minimum to render all the contents.
Code
FIDDLE with the example code to work on. Screenshot below.
I want to make the first row in the right table (.Table-Row--NotResizable) to be the same height as the first row in the left table.
HTML
<div class="TableDisplay">
<table class="Table Table--Natural">
<tr>
<th>Artist</th>
<th>Song</th>
</tr>
<tr>
<td>Prince</td>
<td>Kiss</td>
</tr>
<tr>
<td>Bob Dylan</td>
<td>Idiot Wind</td>
</tr>
</table>
<table class="Table Table--Full">
<tr class="Table-Row--NotResizable">
<th>Artist</th>
<th>Song</th>
</tr>
<tr>
<td>Prince</td>
<td>Kiss</td>
</tr>
<tr>
<td>Bob Dylan</td>
<td>Idiot Wind</td>
</tr>
</table>
</div>
CSS
html,
body {
height: 100%;
min-height: 100%;
}
.TableDisplay {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
height: 100%;
}
.Table {
height: 100%;
border-collapse: collapse;
}
.Table td,
.Table th {
border: 1px solid black;
}
.Table--Full {
height: 100%;
}
.Table--Natural {
height: auto;
}
/* Make this row do not participate in height changes */
.Table-Row--NotResizable {
/* ??? */
}
In fact fixed value in px is exactly what you should use:
.Table-Row--NotResizable {
height: 1px;
}
If you set it to 1px then the browser will resize it to exactly the size needed to fit the content. Table content has to fit into table cell, so the height will not be smaller, and as any (non-empty) content will be higher than 1px it will also not be greater than minimum needed.
I've been scratching my head at this for ages but I finally found a solution that works for me which slightly differs from the current answer.
In order to prevent each <Tr> from resizing to match the <Table> height, I set the height of each of my <Tr> to 1px, however, to stop these rows from resizing, I had to add an empty final <Tr> that does not contain any data to the end of my <Table>. It seems this behaviour is because the <Table> element by default needs the <Tr> elements to sum up to the total height of the table, and the empty <Tr> element stretches to fill this height whilst the rows containing data can be sized to their content.

Html table tie column widths together

I have a table with a fixed number of columns. I would like 3 of the columns to have the same width, but I don't know what it is, as I don't know the width of the other 2. I want the browser to render it as best it can with the one constraint that my three designated columns all have the same width,
Here's an example. I would like columns 2,3,4 all to have the same width, I don't mind what it is, just that they are the same.
<table>
<tr>
<td>Could be short or maybe it could be long</td>
<td class="samewidth">Col2 Long Text</td>
<td class="samewidth">Col3 Some More Long Text</td>
<td class="samewidth">Col4 Some More Long Text and a bit more on top</td>
<td>Could be short or long</td>
<tr>
</table>
I can only do something like I want by having a fixed percentage
td.samewidth {
width: 25%;
overflow: hidden;
}
Here's a fiddle with the best I could do which ties the three columns to have a width of 25%.
https://jsfiddle.net/GrimRob/zugnyzb4/
What I ideally want to do is get rid of width: 25% and put something in its place, but what?
Do it like this:
td.samewidth {
width: 33%;
overflow: hidden;
}
<table>
<tr>
<td>Could be short or maybe it could be long</td>
<td>
<table>
<tr>
<td class="samewidth">Col2 Long Text</td>
<td class="samewidth">Col3 Some More Long Text</td>
<td class="samewidth">Col4 Some More Long Text and a bit more on top</td>
</tr>
</table>
</td>
<td>Could be short or long</td>
</tr>
</table>
Since your "samewidh" columns will be 33% fixed, you won't have trouble with other rows misaligning. At most you'll have to add !important.
Hard to arrive at a set answer for your question, since I am not exactly sure how you want the columns to resize based on content. Flexbox could be an alternative for you. Here is the CSS and a Codepen illustrating, to as best I understood, what you are trying to accomplish.
table {
width: 100%;
border: 1px solid black;
}
tr {
display: flex;
}
td {
border: 1px solid black;
}
td.samewidth {
/* width: 25%; */
overflow: hidden;
flex-grow: 1;
display: inline-block;
flex-basis: 0;
}
td:not(.samewidth) {
flex-basis: content;
}
http://codepen.io/anon/pen/bpobGq
In this example, columns 1 and 5 (not samewidth) determine width based off of the content provided. Columns with the .samewidth class, will grow evenly to fill up the remaining space available in the parent container (in this case the tr).
You really have a lot of options here with how you'd want the columns to resize. You could set a fixed width for columns 1 and 5, size off of content (as in example), or have those grow to fill up space as well. Hopefully this gets you on the right path.
Here's a quick guide on Flexbox. https://css-tricks.com/snippets/css/a-guide-to-flexbox/

CSS 'min-width' increasing width of table cells larger than value

I feel like I'm missing something. I have a <table> set to width: 100%; and I add CSS to prevent a column from becoming narrower than 50px. It is a simple scenario.
My problem is that the column which has min-width applied gets wider even when if it is already wider than the min-width value.
Example of problem: I apply min-width: 50px; to a td element which has a width of 123px, the td increases to a width of 167px. I expected applying min-width: 50px; to an element with a rendered width of 123px to not change the elements width.
My problem can be reproduced by running the below code and following these steps:
Check the width of the first column of the table
Verify that the column has a width greater than 50px
If the first column has a width less than 50px, increase the width of your browser window.
When the first column has a display width greater than 50px, click the button below the table.
The width of the first column should increase, despite not being smaller than the min-width value.
NOTE: Clicking the button will toggle a class applied to the table cells (th and td elements) which form the first column of the table. The class applies min-width: 50px;.
This problem will be considered solved by any solution that does not affect the calculated width of a table cell (th or td) with a calculated width greater than 50px and style property width: auto; when setting min-width: 50px.
I am using Google Chrome Version 46.0.2490.80 m.
Any help/insight would be greatly appreciated. Thank you.
$("button").click(function() {
$("table tr > *:nth-child(1)").toggleClass("min-width-test")
});
table {
width: 100%;
}
table,
table * {
border: 1px solid black;
}
.min-width-test {
min-width: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tr>
<th>First</th>
<th>Second</th>
<th>Third</th>
<th>Fourth</th>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>A</td>
<td>B</td>
<td>C</td>
<td>D</td>
</tr>
</table>
<button>Toggle min-width on first column</button>
CSS has no specification for how min-width and max-width are handled by tables, inline tables, table cells, etc. The behavior you are seeing is how Chrome somewhat arbitrarily handles it.
In CSS 2.1, the effect of 'min-width' and 'max-width' on tables, inline tables, table cells, table columns, and column groups is undefined.
CSS 2.1
This hasn't been changed in any later specs.
As Matt mentioned in the comments, adding min-width is going to increase the size of the column. Tables auto-adjust their size based on their content, and adding a min-width of 50 will thus increase it by 50.
I've copied over your code and made it so you can visualize what's happening. Adding a min-width of 50px is like adding an invisible element of width 50 in there (which I've conveniently made un-invisible and colored red). If you want to counteract that, try removing some margins or padding (although this may break your design--can't tell without seeing exactly what you're doing). Press the "Toggle fix on first column" button to see what I mean.
$("button").click(function() {
$("table tr > *:nth-child(1)").toggleClass("min-width-test")
});
$("#b2").click(function() {
$("table tr > *:nth-child(1)").toggleClass("min-width-test-fixed")
});
table {
width: 100%;
}
table,
table * {
border: 1px solid black;
}
.min-width-test:after {
content: '';
width: 50px;
height: 1px;
background: red;
display: block;
}
.min-width-test-fixed:after {
content: '';
width: 50px;
margin-right: -50px;
height: 1px;
background: green;
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tr>
<th>First</th>
<th>Second</th>
<th>Third</th>
<th>Fourth</th>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>A</td>
<td>B</td>
<td>C</td>
<td>D</td>
</tr>
</table>
<button>Toggle min-width on first column</button>
<button id="b2">Toggle fix on first column</button>

Left align two tables while keeping the first (with fluid width) centered

The page consists of two tables (golf scores). The first, which is always wider than the second, includes two columns of names and therefore its total width will vary from league to league. The second table only contains numbers and its width will be constant.
I want to align the left edge of the two tables while keeping the first table centered.
If I knew the width of the first table then the issue would be simple, use css to set the html width to that of the table and set the two table left margins to 0. But with the width being fluid the tables move with different content.
How do I solve this dilemma?
Here is a fairly easy and robust way of solving this problem.
First, create a wrapper elements to contain the two tables, .wrap in my example.
On .wrap, set display: table and margin: 0 auto. This will force .wrap to take a shrink-to-fit width, and then the margin trick will center the block.
Your two child tables will then be aligned to the left by default.
.wrap {
display: table;
border: 1px dashed blue;
margin: 0 auto;
}
table {
border: 1px dotted blue;
margin: 20px;
}
table td {
border: 1px solid gray;
}
<div class="wrap">
<table>
<tr>
<td>First Wide Column With Long Names</td>
<td>Second Wide Column with Very Long Names</td>
</tr>
</table>
<table>
<tr>
<td>First Short Column</td>
<td>Second Short Column</td>
</tr>
</table>
</div>
You can also see jsfiddle at: http://jsfiddle.net/audetwebdesign/qt5ffzuo/
One way to do this is to add a wrapper around the tables that has display: inline-block. This will make the wrapper shrink to fit around the largest table. It will also allow you to center it horizontally by setting the parent (for example the body) to text-align: center.
To make sure the text in the tables is not also centered, you'll have to set text-align: left to the wrapper contents.
body {
text-align: center;
}
.wrapper {
display: inline-block;
border: 1px solid red;
text-align: left;
}
table, tr, td {
border: 1px solid blue;
}
<div class="wrapper">
<table>
<tr><th>Name</th><th>Score</th></tr>
<tr>
<td>Johnny</td>
<td>12</td>
</tr>
<tr>
<td>Jimmy</td>
<td>12.412.002</td>
</tr>
</table>
<table>
<tr>
<td>6</td>
<td>3</td>
</tr>
<tr>
<td>5</td>
<td>4</td>
</tr>
</table>
</div>

Make cell fill availble space with its text, but not add whitespaces

Conclusion: use JavaScript to calculate max-width of first cell.
Example:
<table>
<tr>
<td style="white-space:nowrap; overflow: hidden">Some text. It maybe very long and should be shortened if there is no more available width in table</td>
<td>This shall always be visible and should not have any space between this and the previous cell, but if the two cells are thinner than the table I want my white-spaces after the end of this cell</td>
</tr>
</table>
What I don't know is the width of cell2 or the width of the table
What I want to achieve is to have a max-width on the first cell, based on available space without using JavaScript. Not sure if it even is possible.
Example where the text is short:
|Some short text|Her comes a new text |
Example where the text is too long:
|This is some text which might s|Her comes a new text|
Its a little hard to determine exactly what you want- perhaps something like this?
HTML
<div>
<span>Something quite long that should exceed the table cell.</span>
<span>here is some moreSomething quite long that should exceed the table cell.Something quite long that should exceed the table cell.</span>
</div>
CSS
div{
margin:0;
padding:0;
display:table;
table-layout: fixed;
width:100%;
max-width:100%;
}
span{
padding 0 20;
margin:0;
display:table-cell;
border:1px solid grey;
}
span:first-child{
white-space: nowrap;
overflow:hidden;
text-overflow: ellipsis;
}
span:last-child{
width:auto;
}
You can use the selector :first-child to select the first <td> inside a table:
table td:first-child {
width: 100%;
}
Is this what you want?
All I did was add a max-width to the cell that you want a max width on.
td:first-child {
max-width: 100px;
overflow: hidden;
white-space: nowrap;
}
Simplest solution without JavaScript is:
<table>
<tr>
<td style="width:80%;">Some text. It maybe very long and should be shortened if there is no more available width in table</td>
<td>This shall always be visible and should not have any space between this and the previous cell, but if the two cells are thinner than the table I want my white-spaces after the end of this cell</td>
</tr>
</table>
Apologies, if I do not understand what exactly you want!