OK, I found a solution. Fiddle here.
Strategy:
Use a hidden table to glean the desired cell widths of the visible table.
Tactic:
In addition to the table that the user shall see, a hidden "shadow" table, with identical content, must be created directly above the visible table.
The shadow table must allow the content to wrap inside the cells (this is default table behavior).
When the page has loaded and at every window resize, show() the shadow table, measure the width of every td in the top row, then hide() the shadow table. Then copy the width values to the corresponding td elements in the visible table, which must have Chris Coyier's truncate applied.
Works in all browsers I've tested, including mobile.
Bonus tips:
- Use
­ to wrap long words if necessary, and to stop words from wrapping. This can be applied only in the shadow table.
- Use
1px more cell padding in the shadow table due to a bug in Internet Explorer - otherwise, IE's visible table sometimes becomes slightly wider than the shadow table.
JavaScript (requiring jQuery):
<script type="text/javascript">
function loadEvents() {
initFluidTables();
}
// Resize fluid table(s)
function resizeFluidTables() {
// Show source cells
$( ".fluid-table-invisible-source" ).show(0);
var fluidTableCellWidth = [];
// Measure (normally invisible) source cells
$( ".fluid-table-invisible-source td" ).each(function( index, value ) {
fluidTableCellWidth[index] = $( this ).width();
});
// Resize (always visible) target cells. Adding 1 pixel due to apparent bug in Firefox.
$( ".fluid-table-visible-target td>i" ).each(function( index, value ) {
$( this ).css({'width': fluidTableCellWidth[index]+1 });
});
// Re-hide source cells
$( ".fluid-table-invisible-source" ).hide();
}
// Create table(s) to be fluid
function initFluidTables() {
// Create a container. Not really necessary, but keeps DOM tidier.
$(".fluid-table").wrap( "<div></div>" );
// This looks like a mess. What it does, is that .fluid-table duplicates itself, and each sibling gets a different class.
$(".fluid-table").each(function() {
$( this ).clone().appendTo( $( this ).addClass( "fluid-table-invisible-source" ).parent() ).addClass( "fluid-table-visible-target" );
});
// Add truncating element inside target cells
$(".fluid-table-visible-target td").wrapInner( "<i></i>");
// Truncate table contents at first drawing of the DOM and every time the window resizes
resizeFluidTables();
$( window ).resize(function() {
resizeFluidTables();
});
}
</script>
CSS:
.fluid-table td { padding-right: 5px; }
.fluid-table td:nth-child(odd) { color: #aaa; }
.fluid-table-visible-target td>i {
font-style: inherit;
white-space: nowrap;
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
/* source slighly more padded than target due to IE bug */
.fluid-table-invisible-source td:nth-child(even) {
padding-right: 10px;
}
.fluid-table-visible-target td:nth-child(even) {
padding-right: 9px;
}
Sample table:
Note use of ­ and to indicate where you (do not) want the text to truncate.
<table class="fluid-table">
<tr>
<td>Avail­able <i>until</i>:</td><td>No expiry date</td><td>Avail­ability:</td><td>Worldwide</td><td></td>
</tr><tr>
<td>Year:</td><td>2016</td><td>Length:</td><td>29 minutes</td><td></td>
</tr><tr>
<td>First broad­cast:</td><td>Feb 2</td><td>Last broad­cast:</td><td>Feb 3</td><td></td>
</tr>
</table>