ravb79 ravb79 - 9 days ago 6
HTML Question

Unaligned borders/width fixed table header and table

The idea:

Create an adwords-style table for displaying data, complete with fixed, fluid-like header.

The method:

Clone a table header, give its cells the corresponding widths from the actual table, fix to top when the table header scrolls out of the viewport (vertically) and scrolling the header left when scrolling horizontally.

The problem:

As described in the title. Apparently webkit and IE calculate cells differently (I guess?) from Mozilla and Opera (in which it all works fine, though every so often when the window has just the "right" width, the header protrudes one pixel to the right).

The HTML:

<table id="table-frame">
<col id="left-col-bg">
<tr>
<td id="top-row" colspan="2">
<div id="header-container">primary-nav</div><!-- #header-container -->
</td>
</tr>
<tr>
<td id="left-col">context-nav</td>
<td id="right-col">
<div id="content">
<div id="page-header">
content header
</div><!-- page-header -->
<div id="data-container">
<div id="keyword-container">
<table class="data-table" id="kw-table">
<colgroup><col><col><col><col><col><col><col><col><col><col><col></colgroup>
<thead class="data-header">
<tr class="desc-row">
<th class="th0 a-l"><a href="">variable</a></th>
<th class="th1 a-l w-10"><a href="">topparent</a></th>
<th class="th2 a-l w-10"><a href="">subparentextended</a></th>
<th class="th3 a-l w-10"><a href="">verion</a></th>
<th class="th4 a-r w-10"><a href="">calls</a></th>
<th class="th5 a-r w-10"><a href="">interest</a></th>
<th class="th6 a-r w-5"><a href="">elem attr</a></th>
<th class="th7 a-r w-10"><a href="">state</a></th>
<th class="th8 a-r w-10"><a href="">costs</a></th>
<th class="th9 a-r w-5"><a href="">return</a></th>
<th class="th10 a-r w-5"><a href="">est. bid first call</a></th>
</tr>
</thead>
<tfoot class="data-footer">
<tr class="sum-row">
<td class="a-l">summary</td>
<td class="a-l">&nbsp;</td>
<td class="a-l">&nbsp;</td>
<td class="a-l">&nbsp;</td>
<td class="a-r">100.000</td>
<td class="a-r">1.234.567.890</td>
<td class="a-r">0,0</td>
<td class="a-r">&nbsp;</td>
<td class="a-r fin">$ 1.123.456,00</td>
<td class="a-r">20.000</td>
<td class="a-r fin">$ 12,50</td>
</tr>
</tfoot>
<tbody class="data-body">
<tr class="data-row first-row">
<td class="a-l">variable cell plus</td>
<td class="a-l"><a href="">top parent</a></td>
<td class="a-l"><a href="">sub parent</a></td>
<td class="a-l">type</td>
<td class="a-r">2.000</td>
<td class="a-r">15.000</td>
<td class="a-r">10,50</td>
<td class="a-r">active</td>
<td class="a-r fin">$ 120,50</td>
<td class="a-r">50</td>
<td class="a-r fin">$ 12,50</td>
</tr>
<tr class="data-row alt-row">
<td class="a-l">variable cells</td>
<td class="a-l"><a href="">basic top parent</a></td>
<td class="a-l"><a href="">sub parent</a></td>
<td class="a-l">type</td>
<td class="a-r">2.000</td>
<td class="a-r">15.000</td>
<td class="a-r">10,50</td>
<td class="a-r">active</td>
<td class="a-r fin">$ 120,50</td>
<td class="a-r">50</td>
<td class="a-r fin">$ 12,50</td>
</tr>
<tr class="data-row">
<td class="a-l">variable to cell</td>
<td class="a-l"><a href="">top parent</a></td>
<td class="a-l"><a href="">sub parent</a></td>
<td class="a-l">type</td>
<td class="a-r">2.000</td>
<td class="a-r">15.000</td>
<td class="a-r">10,50</td>
<td class="a-r">active</td>
<td class="a-r fin">$ 120,50</td>
<td class="a-r">50</td>
<td class="a-r fin">$ 12,50</td>
</tr>
<tr class="data-row alt-row">
<td class="a-l">cell</td>
<td class="a-l"><a href="">top parent</a></td>
<td class="a-l"><a href="">sub parent example</a></td>
<td class="a-l">type</td>
<td class="a-r">2.000</td>
<td class="a-r">15.000</td>
<td class="a-r">10,50</td>
<td class="a-r">active</td>
<td class="a-r fin">$ 120,50</td>
<td class="a-r">50</td>
<td class="a-r fin">$ 12,50</td>
</tr>
<tr class="data-row">
<td class="a-l">variablecellextendedversion</td>
<td class="a-l"><a href="">top parent</a></td>
<td class="a-l"><a href="">sub parent</a></td>
<td class="a-l">type</td>
<td class="a-r">2.000</td>
<td class="a-r">15.000</td>
<td class="a-r">10,50</td>
<td class="a-r">active</td>
<td class="a-r fin">$ 120,50</td>
<td class="a-r">50</td>
<td class="a-r fin">$ 12,50</td>
</tr>
<tr class="data-row last-row alt-row">
<td class="a-l">variable cell</td>
<td class="a-l"><a href="">top parent</a></td>
<td class="a-l"><a href="">sub parent</a></td>
<td class="a-l">type</td>
<td class="a-r">2.000</td>
<td class="a-r">15.000</td>
<td class="a-r">10,50</td>
<td class="a-r">active</td>
<td class="a-r fin">$ 120,50</td>
<td class="a-r">50</td>
<td class="a-r fin">$ 12,50</td>
</tr>
</tbody>
</table>
</div><!-- keyword-container -->
</div><!-- data-container -->
</div><!-- content -->
</td>
</tr>
<tr>
<td id="bottom-row" colspan="2"><div id="footer-container">footer</div></td>
</tr>
</table>


The CSS:

html, body {
border:0;
height:100%;
margin:0;
padding:0;
outline:0;
width:100%;
}

body {
background:#fff;
color:#000;
font:400 14px/20px Arial, Helvetica, sans-serif;
text-align:left;
}
small {
font-size:.75em;
}
b, strong {
font-weight:600;
}
p {
margin:0 0 10px;
}
p:last-child {
margin:0;
}
.clearfix:before, .clearfix:after {
content:"";
display:table;
}
.clearfix:after {
clear:both;
}
.clearfix {
zoom:1;
}

#header-container {
background:#fff;
border-bottom:1px solid #ddd;
}

#table-frame {
height:100%;
width:100%;
min-width:960px;
}
#table-frame {
border-collapse:collapse;
}
#top-row,
#bottom-row,
#left-col,
#right-col {
border-spacing:0;
margin:0;
padding:0;
vertical-align:top;
}
#left-col-bg {
background:#fff;
border-right:1px solid #ddd;
width:220px;
}
#top-row {
height:1px;
vertical-align:top;
}
#bottom-row {
height:1px;
vertical-align:bottom;
}
#left-col,
#right-col {
height:auto;
vertical-align:top;
}
#left-col {
width:220px;
min-width:220px;
}
#menu-bar {
font-size:12px;
padding:10px 0 0;
}
#right-col {
width:auto;
}
#content {

}
#page-header {
border-bottom:1px solid #ddd;
padding:0 10px;
}

#footer-container {
background:#363636;
color:#e6e6e6;
font-size:12px;
font-weight:600;
padding:10px;
}

table {display:table;}
td, th {display:table-cell;}

#data-container {
}
#keyword-container {
position:relative;
margin:10px;
}
.data-table {
border-width:1px 0 0 1px;
border-style:solid;
border-color:#ccc #ccc #aaa #ccc;
border-collapse:collapse !important;
border-spacing:0;
max-width:100%;
padding:0;
position:relative;
margin:0;
table-layout:fixed;
overflow:visible;
}
.data-table th, .data-table td {
border-width:0 1px 1px 0;
border-width:0;
border-style:solid;
border-spacing:0;
font-size:11px;
overflow:visible;
padding:4px 6px;
vertical-align:baseline;

outline-width:1px;
outline-style:solid;
outline-color:red;
}
.data-table td {
font-weight:normal;
border-color:#ddd;
}
.data-table th {
background:#eee;
font-weight:bold;
border-color:#ccc #ccc #aaa #ccc;
}
.data-body a {
color:#009;
}
.data-body a:hover {
text-decoration:underline;
}

.data-table a, .data-table span {
}

.sum-row td {
background:#eee;
color:#333;
font-weight:bold;
}
.first-row td {
}
.last-row td {
border-bottom:1px solid #bbb;
}
.calc-row td {
border-color:#bbb;
}

.cloned-table {
margin-left:0;
position:absolute;
top:0;
z-index:9;
}
.cloned-table.fixed {
position:fixed;
}

.data-header a span {font-weight:bold;}
.a-l {text-align:left;}
.a-r {text-align:right !important;}
.fin {white-space:nowrap;}
.w-a {}
.w-5 {width:5%;}
.w-10 {width:10%;}
.w-20 {width:20%;}
.w-30 {width:30%;}


The JQ:

$(document).ready(function(){
initTable();
});
$(window).resize(function(){
waitForFinalEvent(function(){
resizeTable();
}, 250, 'tableResize');
});

$(window).scroll(function(){
var fromTop = $('#kw-table').offset().top;

if ($(window).scrollTop() > fromTop){
$('.cloned-table').addClass('fixed');
} else {
$('.cloned-table').removeClass('fixed').css({'margin-left':'0'});
}
positionTable();
});

function resizeTable(){

$('.data-table, .data-table th, .data-table td').removeAttr('style');
var tdWidth = '';
var tdClass = '';
var tableWidth=0;
$('#kw-table th').each(function(){
tdClass = '.'+$(this).attr('class').split(' ')[0];
tdWidth = Math.floor($(this).width());
$('.cloned-table '+tdClass).css({'width':tdWidth});
tableWidth = tableWidth + tdWidth;
});

$('.cloned-table .data-table').width(Math.floor($('#kw-table').width()));
}

function positionTable(){
var leftScroll = $(document).scrollLeft();
$('.cloned-table.fixed').css({'margin-left':-leftScroll});
}

function initTable(){
var clonedHeader = $('.data-header').html();
clonedHeader = '<div class="cloned-table"><table class="data-table"><thead class="data-header">' +
clonedHeader +
'</thead></table></div>'
$('#keyword-container').prepend(clonedHeader);

resizeTable();
}

var waitForFinalEvent = (function () {
var timers = {};
return function (callback, ms, uniqueId) {
if (!uniqueId) {
uniqueId = 'tableResize';
}
if (timers[uniqueId]) {
clearTimeout (timers[uniqueId]);
}
timers[uniqueId] = setTimeout(callback, ms);
};
})();


The fiddle: http://jsfiddle.net/jL9hF/

The remarks:


  • Setting the table-width to 100% makes the text from cells bleed into the adjacent cell in certain browsers;

  • Certain cells have a percentual width because they would otherwise take up too much space (ie. the last column in the table "est. bid first call") shouldn't "take up" space from other "more important" columns, such as the first column;

  • The entire page has been cast in a table for layout purposes. Managing this layout would make the html/css unnecessarily heavy and bulky;

  • Support is for all modern browsers and IE8+.



I've tried numerous approaches thusfar, but since my knowledge of js/jq is so limited, I'm unable to find a suitable solution to this problem.

Please help?

Answer

Finally solved. The problem was apparently a result of jquery's .width function rounding sub-pixels.

Applying

document.defaultView.getComputedStyle(e, null).width

solved it.