The code example below creates a table with 6 rows. The last two rows are empty.
The JavaScript code finds and displays correctly the number of rows in a table.
I would like to find the row number of the first row with empty cells. In this example it will be row (4) (counting from 0). I tried several solutions and they did not work.
Your help is appreciated.
Thanks,
Menachem
<!DOCTYPE html>
<html>
<head>
<style>
table, td {
border: 1px solid black;
}
</style>
</head>
<body>
<table id="myTable">
<tr>
<td>cell 1</td>
<td>cell 2</td>
</tr>
<tr>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 4</td>
<td>cell 5</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
<br>
<script>
var x = document.getElementById("myTable").rows.length;
alert ("Number of rows in the table is " + x);
</script>
</body>
</html>
You can use combination of :has() and :not().
td:not(:empty) get td which is not empty
tr:not(:has(td:not(:empty))) selects all tr which is not contains any non empty td
tr:not(:has(td:not(:empty))):first gets the first tr from them
var index = $("#myTable tr:not(:has(td:not(:empty))):first").index();
console.log(index);
table,td {
border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table id="myTable">
<tr>
<td>cell 1</td>
<td>cell 2</td>
</tr>
<tr>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 4</td>
<td>cell 5</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
<br>
<script>
var x = document.getElementById("myTable").rows.length;
alert("Number of rows in the table is " + x);
</script>
I would like to find the row number of the first row with empty cells
You can use :has(), adjacent sibling selector + to match td:empty, that has next element sibling that is td:empty, :first, index(). The index of the first tr element which has a child td element without child nodes would be 3
$("#myTable tr:has(td:empty + td:empty):first").index()
<!DOCTYPE html>
<html>
<head>
<style>
table,
td {
border: 1px solid black;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
</head>
<body>
<table id="myTable">
<tr>
<td>cell 1</td>
<td>cell 2</td>
</tr>
<tr>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 4</td>
<td>cell 5</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
<br>
<script>
var x = document.getElementById("myTable").rows.length;
alert("Number of rows in the table is " + x);
console.log($("#myTable tr:has(td:empty + td:empty):first").index());
</script>
</body>
</html>
$("#myTable tr").find('td').filter(function() {
return $(this).text() == '' ;
}).addClass('empty');
table,
td {
border: 1px solid black;
}
.empty {
background-color: red
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="myTable">
<tr>
<td>cell 1</td>
<td>cell 2</td>
</tr>
<tr>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 4</td>
<td>cell 5</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
Use .filter()
You can use a test inside a .filter() call that checks the number of empty td's against the number of all td's in a tr. Filter out all those that have any non-empty td, call .first() to find the first one in the filtered set, and .index() to get the index
var index = $("#myTable tr").filter(function(){
return $("td",this).length == $("td:empty",this).length;;
}).first().index();
console.log("Empty row index: "+index);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table id="myTable">
<tr>
<td>cell 1</td>
<td>cell 2</td>
</tr>
<tr>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 4</td>
<td>cell 5</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
I have an HTML table which is dynamically populated from data fetched from a server. Since I don't know the schema or the number of rows before hand, I have its, and its row's & cell's position defined as relative.
Now once the table is populated, I want to delete a set of rows from it(specified by an array of row indexes). I am trying for the following animation for these row deletions.
The rows which are supposed to be deleted fade out leaving blank space in the table.->The other rows slide up to fill the space created between them.
I have tried out following things:
Simply fading out or sliding up the rows to be deleted using jquery.
The problem with this is, the animation is jerky, and all I can see is the bunch of rows disappearing at once.
Setting the 'position' of all other rows except the ones to be deleted as fixed, Fading out the rows to be deleted, and then sliding up the rows which were fixed.
The problem with this is - Since the rows were relatively placed, once I make them fixed they all lose all their previous styles, contract, and lose the well defined structure and overwriting each other.
What would be the best way to implement the required kind of animation?
Also, how would one go about specifying a bunch of rows (specified by a list of indexes) for a jQuery selector. Right now I am creating the tr:nth-of-type(i) selector for each element of the list, and concatenating them in a large string separated by ',', and using this as the selector for all the rows. Is there a better way to do this?
jsBin demo
Don't animate table elements. Ever.
Instead animate DIV elements inside your row cells. Once they faded out and animated to height 0, than you're free to remove the parent TR
Example:
$("button").on("click", function(){
// Table with no DIV elements (Animate ROWS)
$("#noDivs tr:eq(1)").animate({opacity:0}, 800, function(){
$(this).slideUp();
});
// Table with DIVs (Animate DIV)
$("#divs tr:eq(1) div").animate({opacity:0}, 800, function(){
$(this).slideUp();
});
});
table{
border-collapse: separate;
border-spacing: 0px;
}
table td{
padding:0; margin:0;
}
table#noDivs td,
table#divs div{
border:1px solid #ddd;
padding:10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Delete rows 2</button>
<br><br>
Animate DIV instead:
<table id="divs">
<tr>
<td><div>Cell 1</div></td>
<td><div>Cell 2</div></td>
<td><div>Cell 3</div></td>
<td><div>Cell 4</div></td>
</tr>
<tr>
<td><div>Cell 1</div></td>
<td><div>Cell 2</div></td>
<td><div>Cell 3</div></td>
<td><div>Cell 4</div></td>
</tr>
<tr>
<td><div>Cell 1</div></td>
<td><div>Cell 2</div></td>
<td><div>Cell 3</div></td>
<td><div>Cell 4</div></td>
</tr>
<tr>
<td><div>Cell 1</div></td>
<td><div>Cell 2</div></td>
<td><div>Cell 3</div></td>
<td><div>Cell 4</div></td>
</tr>
</table>
Animate TR (Issue)
<table id="noDivs">
<tr>
<td>Cell 1</td>
<td>Cell 2</td>
<td>Cell 3</td>
<td>Cell 4</td>
</tr>
<tr>
<td>Cell 1</td>
<td>Cell 2</td>
<td>Cell 3</td>
<td>Cell 4</td>
</tr>
<tr>
<td>Cell 1</td>
<td>Cell 2</td>
<td>Cell 3</td>
<td>Cell 4</td>
</tr>
<tr>
<td>Cell 1</td>
<td>Cell 2</td>
<td>Cell 3</td>
<td>Cell 4</td>
</tr>
</table>
you should add/remove a class and use CSS to animate.
here an example (only css for test ONLY) to see how it would/could work
table {
width:600px;
border:solid;
}
td {
border:solid;
}
/* demo purpose , instead js */
td {
pointer-events:none;
font-size:1.2em;
opacity:1;
}
tr:focus td {
font-size:0;
border:solid 0 transparent;
opacity:0;
transition:1s;/*with steps or css animation is fine too */
}
<table>
<tr tabindex="0">
<td>hide</td>
<td>my</td>
<td>row</td>
</tr>
<tr tabindex="0">
<td>hide</td>
<td>my</td>
<td>row</td>
</tr>
<tr tabindex="0">
<td>hide</td>
<td>my</td>
<td>row</td>
</tr>
<tr tabindex="0">
<td>hide</td>
<td>my</td>
<td>row</td>
</tr>
<tr tabindex="0">
<td>hide</td>
<td>my</td>
<td>row</td>
</tr>
<tr tabindex="0">
<td>hide</td>
<td>my</td>
<td>row</td>
</tr>
<tr tabindex="0">
<td>hide</td>
<td>my</td>
<td>row</td>
</tr>
<tr tabindex="0">
<td>hide</td>
<td>my</td>
<td>row</td>
</tr>
</table>
I want to invert table tbody rows with jQuery.
WHAT I HAVE:
<table width="630" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td>TITLE A</td>
<td>TITLE B</td>
(...) continue in jsfiddle.
Here what I have and what I want: http://jsfiddle.net/ZaUrP/1/
fiddle
pretty much the same as the other guy, only I use .detach() which is guarunteed to keep any crazy events that were attached to the trs intact. I also use $.makeArray to avoid reversing any of the proto stuff on the base jQuery object.
$(function(){
$("tbody").each(function(elem,index){
var arr = $.makeArray($("tr",this).detach());
arr.reverse();
$(this).append(arr);
});
});
Try this:-
Get the array of trs from tbody using .get() and use Array.reverse to reverse the elements and assign it back.
var tbody = $('table tbody');
tbody.html($('tr',tbody).get().reverse());
Fiddle
In case you have events to tr or any containing elements you could just attach it using delegation, so that the reversed elements also get them delegated.
Demo
$('tbody').each(function(){
var list = $(this).children('tr');
$(this).html(list.get().reverse())
});
Demo --> http://jsfiddle.net/ZaUrP/5/
I wrote a jQuery plugin called $.reverseChildren which will reverse all the specified children of a given element. Credit goes to DefyGravity for his insightful and intriguing use of $.makeArray.
I have not only reversed the rows of a table, but also the columns.
(function($) {
$.fn.reverseChildren = function(childSelector) {
this.each(function(el, index) {
var children = $.makeArray($(childSelector, this).detach());
children.reverse();
$(this).append(children);
});
return this;
};
}(jQuery));
$(function() {
var tableCopy = $('#myTable').clone(true).attr('id', 'myTableCopy').appendTo(
$('body').append('<hr>').append($('<h1>').html('Reversed Table')));
tableCopy.find('tr').reverseChildren('th, td'); // Reverse table columns
tableCopy.find('tbody').reverseChildren('tr'); // Reverse table rows
});
* { font-family: "Helvetica Neue", Helvetica, Arial; }
h1 { font-size: 16px; text-align: center; }
table { margin: 0 auto; }
th { background: #CCC; padding: 0.25em; }
td { border: 1px solid #CCC; padding: 5px; }
hr { margin: 12px 0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<h1>Original Table</h1>
<table id="myTable" width="320" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr> <th>Header A</th> <th>Header B</th> <th>Header C</th> </tr>
</thead>
<tbody>
<tr> <td>Data 1A</td> <td>Data 1B</td> <td>Data 1C</td> </tr>
<tr> <td>Data 2A</td> <td>Data 2B</td> <td>Data 2C</td> </tr>
<tr> <td>Data 3A</td> <td>Data 3B</td> <td>Data 3C</td> </tr>
<tr> <td>Data 4A</td> <td>Data 4B</td> <td>Data 4C</td> </tr>
</tbody>
</table>
I know this is late, but it can help some other users looking for an answer
function reverseTable() {
var table = document.getElementById("table")
var trContent = []
for (var i = 0, row; row = table.rows[i]; i++) {
trContent.push(row.innerHTML)
}
trContent.reverse()
for (var i = 0, row; row = table.rows[i]; i++) {
row.innerHTML = trContent[i]
}
}
table {border-collapse: collapse}
table, td {border: 1px solid black}
<html>
<body onload="reverseTable()">
Original Table
<table>
<tbody>
<tr>
<td>Cell 1,1</td>
<td>Cell 1,2</td>
<td>Cell 1,3</td>
</tr>
<tr>
<td>Cell 2,1</td>
<td>Cell 2,2</td>
<td>Cell 2,3</td>
</tr>
<tr>
<td>Cell 3,1</td>
<td>Cell 3,2</td>
<td>Cell 3,3</td>
</tr>
</tbody>
</table>
<br>
Reversed Table
<table id="table">
<tbody>
<tr>
<td>Cell 1,1</td>
<td>Cell 1,2</td>
<td>Cell 1,3</td>
</tr>
<tr>
<td>Cell 2,1</td>
<td>Cell 2,2</td>
<td>Cell 2,3</td>
</tr>
<tr>
<td>Cell 3,1</td>
<td>Cell 3,2</td>
<td>Cell 3,3</td>
</tr>
</tbody>
</table>
</body>
</html>
I have a large dynamic table created from database data. I need the column heading rows to remain fixed and scroll the rows an necessary.
I have tried numerous scripts all over the web trying to get this to work properly. I'd like to keep this simple and easy on browser, since some target computers are rather lame.
Here is what I'm working with:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Sample</title>
</head>
<body>
<br><br><br><br>
<table id="A" border="0" width="95%" cellspacing="0" cellpadding="0" align="center" class="base">
<tr bgcolor='gray'>
<td>
<br><br><br>
need the blue column heading rows to remain fixed, and scroll the green rows:<br>
<table id="XYZ" border="1" width="625" cellspacing="0" cellpadding="0" align="center" class="base">
<thead>
<tr>
<th width="50px" bgcolor="DeepSkyBlue" align="center" valign="middle">Col 1a</th>
<th width="50px" bgcolor="DeepSkyBlue" align="center" valign="middle">Col 1b</th>
<th width="75px" bgcolor="DeepSkyBlue" align="center" valign="middle">Col 1c</th>
<th width="100px" style="border-left:medium solid black;" colspan="3" bgcolor="DeepSkyBlue" align="center" valign="middle"><b>Col 2</th>
<th width="100px" style="border-left:medium solid black;" colspan="1" bgcolor="DeepSkyBlue" align="center" valign="middle"><b>Col 3</th>
<th width="150px" style="border-left:medium solid black;" colspan="5" bgcolor="DeepSkyBlue" align="center" valign="middle"><b>Col 4<br>more<br>more</th>
<th width="100px" style="border-left:medium solid black;" colspan="1" bgcolor="DeepSkyBlue" align="center" valign="middle"><b>Col 5</th>
</tr>
<tr>
<th bgcolor="DeepSkyBlue" colspan="3" align="center" valign="middle">Col 1</th>
<th bgcolor="DeepSkyBlue" style="border-left:medium solid black;" align="center" valign="middle">A</th>
<th bgcolor="DeepSkyBlue" align="center" valign="middle">B</th>
<th bgcolor="DeepSkyBlue" align="center" valign="middle">C</th>
<th bgcolor="DeepSkyBlue" style="border-left:medium solid black;" align="center">1</th>
<th bgcolor="DeepSkyBlue" style="border-left:medium solid black;" align="center" valign="middle">4-a</th>
<th bgcolor="DeepSkyBlue" align="center" valign="middle">4-b</th>
<th bgcolor="DeepSkyBlue" align="center" valign="middle">4-c</th>
<th bgcolor="DeepSkyBlue" align="center" valign="middle">4-d</th>
<th bgcolor="DeepSkyBlue" align="center" valign="middle">4-e</th>
<th bgcolor="DeepSkyBlue" style="border-left:medium solid black;" align="center">Z</th>
</tr>
</thead>
<tbody>
<tr>
<td bgcolor="PaleGreen" colspan="3" align="center" valign="middle">Col 1<br>more</td>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center" valign="middle">A</th>
<td bgcolor="PaleGreen" align="center" valign="middle">B</th>
<td bgcolor="PaleGreen" align="center" valign="middle">C</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center">1</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center" valign="middle">4-a</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-b</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-c</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-d</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-e</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center"><input type="text" NAME="b[1]" size="3"></th>
</tr>
<tr>
<td bgcolor="LimeGreen" align="center" valign="middle">Col 1a</td>
<td bgcolor="LimeGreen" colspan="2" align="center" valign="middle">Col 1b+c</td>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center" valign="middle">A</th>
<td bgcolor="LimeGreen" align="center" valign="middle">B</th>
<td bgcolor="LimeGreen" align="center" valign="middle">C</th>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center">1</th>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center" valign="middle">4-a</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-b</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-c</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-d</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-e</th>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center"><input type="text" NAME="b[2]" size="3"></th>
</tr>
<tr>
<td bgcolor="PaleGreen" colspan="2" align="center" valign="middle">Col 1a+b</td>
<td bgcolor="PaleGreen" align="center" valign="middle">Col 1c</td>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center" valign="middle">A</th>
<td bgcolor="PaleGreen" align="center" valign="middle">B</th>
<td bgcolor="PaleGreen" align="center" valign="middle">C</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center">1</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center" valign="middle">4-a</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-b</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-c</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-d</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-e</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center"><input type="text" NAME="b[3]" size="3"></th>
</tr>
<tr>
<td bgcolor="LimeGreen" align="center" valign="middle">Col 1a</td>
<td bgcolor="LimeGreen" colspan="2" align="center" valign="middle">Col 1b+c<br>more</td>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center" valign="middle">A</th>
<td bgcolor="LimeGreen" align="center" valign="middle">B</th>
<td bgcolor="LimeGreen" align="center" valign="middle">C</th>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center">1</th>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center" valign="middle">4-a</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-b</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-c</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-d</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-e</th>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center"><input type="text" NAME="b[4]" size="3"></th>
</tr>
<tr>
<td bgcolor="PaleGreen" colspan="2" align="center" valign="middle">Col 1a+b</td>
<td bgcolor="PaleGreen" align="center" valign="middle">Col 1c</td>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center" valign="middle">A</th>
<td bgcolor="PaleGreen" align="center" valign="middle">B</th>
<td bgcolor="PaleGreen" align="center" valign="middle">C</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center">1</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center" valign="middle">4-a</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-b</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-c</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-d</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-e</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center"><input type="text" NAME="b[5]" size="3"></th>
</tr>
<tr>
<td bgcolor="LimeGreen" align="center" valign="middle">Col 1a</td>
<td bgcolor="LimeGreen" colspan="2" align="center" valign="middle">Col 1b+c</td>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center" valign="middle">A</th>
<td bgcolor="LimeGreen" align="center" valign="middle">B</th>
<td bgcolor="LimeGreen" align="center" valign="middle">C</th>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center">1</th>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center" valign="middle">4-a</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-b</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-c</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-d</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-e</th>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center"><input type="text" NAME="b[6]" size="3"></th>
</tr>
<tr>
<td bgcolor="PaleGreen" colspan="2" align="center" valign="middle">Col 1a+b</td>
<td bgcolor="PaleGreen" align="center" valign="middle">Col 1c</td>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center" valign="middle">A</th>
<td bgcolor="PaleGreen" align="center" valign="middle">B</th>
<td bgcolor="PaleGreen" align="center" valign="middle">C</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center">1</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center" valign="middle">4-a</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-b</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-c</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-d</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-e</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center"><input type="text" NAME="b[7]" size="3"></th>
</tr>
<tr>
<td bgcolor="LimeGreen" align="center" valign="middle">Col 1a</td>
<td bgcolor="LimeGreen" colspan="2" align="center" valign="middle">Col 1b+c<br>more<br>more</td>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center" valign="middle">A</th>
<td bgcolor="LimeGreen" align="center" valign="middle">B</th>
<td bgcolor="LimeGreen" align="center" valign="middle">C</th>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center">1</th>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center" valign="middle">4-a</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-b</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-c</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-d</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-e</th>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center"><input type="text" NAME="b[8]" size="3"></th>
</tr>
<tr>
<td bgcolor="PaleGreen" colspan="2" align="center" valign="middle">Col 1a+b</td>
<td bgcolor="PaleGreen" align="center" valign="middle">Col 1c</td>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center" valign="middle">A</th>
<td bgcolor="PaleGreen" align="center" valign="middle">B</th>
<td bgcolor="PaleGreen" align="center" valign="middle">C</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center">1</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center" valign="middle">4-a</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-b</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-c</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-d</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-e</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center"><input type="text" NAME="b[9]" size="3"></th>
</tr>
<tr>
<td bgcolor="LimeGreen" align="center" valign="middle">Col 1a</td>
<td bgcolor="LimeGreen" colspan="2" align="center" valign="middle">Col 1b+c<br>more<br>more</td>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center" valign="middle">A</th>
<td bgcolor="LimeGreen" align="center" valign="middle">B</th>
<td bgcolor="LimeGreen" align="center" valign="middle">C</th>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center">1</th>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center" valign="middle">4-a</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-b</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-c</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-d</th>
<td bgcolor="LimeGreen" align="center" valign="middle">4-e</th>
<td bgcolor="LimeGreen" style="border-left:medium solid black;" align="center"><input type="text" NAME="b[8]" size="3"></th>
</tr>
<tr>
<td bgcolor="PaleGreen" colspan="2" align="center" valign="middle">Col 1a+b</td>
<td bgcolor="PaleGreen" align="center" valign="middle">Col 1c<br>more<br>more<br>more<br>more<br>more<br>more<br>more<br>more<br>more<br>more</td>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center" valign="middle">A</th>
<td bgcolor="PaleGreen" align="center" valign="middle">B</th>
<td bgcolor="PaleGreen" align="center" valign="middle">C</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center">1</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center" valign="middle">4-a</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-b</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-c</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-d</th>
<td bgcolor="PaleGreen" align="center" valign="middle">4-e</th>
<td bgcolor="PaleGreen" style="border-left:medium solid black;" align="center"><input type="text" NAME="b[9]" size="3"></th>
</tr>
</tbody>
</table>
<br><br><br><br>
</td>
</tr>
</table>
<br><br><br><br><br><br>
</body>
</html>
You can have the header row(s) in a separate table to the data rows using the same colgroup settings in both.
The below works fine in IE9, FF14.01 and Chrome 20.0.1132.57.
<table border="1">
<colgroup>
<td width="100px">Column 1</td>
<td width="100px">Column 2</td>
<td width="100px">Column 3</td>
<td width="16px" style="background-color: gray;"><td>
</colgroup>
</table>
<div style="position: absolute; height:75px; overflow-y:scroll; overflow-x:auto">
<table border="1">
<colgroup>
<td width="100px"></td>
<td width="100px"></td>
<td width="100px"></td>
</colgroup>
<tbody>
<tr>
<td>Row 1 - Cell 1</td>
<td>Row 1 - Cell 2</td>
<td>Row 1 - Cell 3</td>
</tr>
<tr>
// rest omitted, see DEMO for full table
</tr>
</tbody>
</table>
</div>
See DEMO
Edit -- August 3rd 2012
The only way I was able to get it to work was with a bit of trickery alright.
I separated the header as in the first example. But due to the extremly custom multiple different widths in the header, the most reliable way, keeping everything aligned, was to copy the th rows also into the second table but without text inside. That made them "invisible" but forced the columns in the second table to align as expected.
See DEMO
It feels a bit hackish and I'm sure there is a proper solution but it seems to work well in the meantime.
Edit -- August 7th 2012
Is there any way to "box" this entire table construct up in up 100% of
the screen width and change the height for height:150px; to something
more dynamic, like $(window).height()-200
There surely is a more elegant way of doing it but I was able to make the table more dynamic.
The width was less of an issue as I capped the toal width at about 95% and hard-capped the table(s) at a min-width: 600px; for the header and a min-width: 584px for the body, thus ensuring the table stays aligned at all times.
For the dynamic height I used jQuery, binding a resize function to the windows resize event:
$(document).ready(function() {
resizeTableHeight();
$(window).on("resize.resizeTableHeight", function() {
resizeTableHeight();
});
});
function resizeTableHeight() {
var headerHeight = $("#tableHeaderContainer").height();
var documentHeight = $(document).height();
var spacingHeight = 50;
$("#tableBodyContainer").height(documentHeight - headerHeight - spacingHeight);
}
When you open the fiddle the original height of the fiddle window will most likely be to high to see the dynamics. Just move the divider and shrink the view to see the re-sizing at work.
Do not forget to unbind that event when you are not showing the grid as it still will execute on resize every time.
See dynamic-grid DEMO
You will notice some styles are in the css (top-right) in the fiddle while others are not. My CSS is not very strong and some styles when I moved them from the elements into the CSS area started to be irgnored. I moved into CSS what I could and left the rest hard-coded not to break it. I'm sure someone working with CSS everyday will be able to sort that out for you.
I also added some ids to some elements for CSS and for the jQuery as required.
I suppose for the CSS it can use classes instead. I leave that to you.
Summary
I'm sure there is a way more elegant way to achieve what you want and probably some script-wiz got a plugin for it. Until then this seems to work. What could also happen is that the columns start miss-aligning close to 600 pixels again if a lot of long data is entered into the columns but as stated, this is a very personalised solution and you may need to add some dynamic calculations for some widths with jQuery over time.
Edit -- August 9th 2012
Regarding setting the width of a td I mentioned in the comments. I fixed the issue with the long text in the first column using the classes I mentioned. Works in IE, FF and Chrome.
See DEMO
I used the logic mentioned in the comments. You might find a much better naming convention. I simply used the main-column+ a on/off switch per sub-column. this works out as follows for column 1 styles:
.col01-000{
width: 0px;
}
.col01-001{
width: 75px;
}
.col01-010, .col01-100{
width: 50px;
}
.col01-011, .col01-101{
width: 125px;
}
.col01-110{
width: 100px;
}
I have assigned the first td a class of col01-100 which means 50px.
The second td has now a class of col01-011, indicating this td still belongs into the first main column but has a width of sub-column 2 (50px) and 3(75px). This adds up to 125px.
I hope this makes sense but if not I gladly continue the discussion in a chat and work the measurements out with you if that is something you want to apply.
So far I can see the measurements to be:
Col01
Col1 = colspan 3 over 175px
3 Sub-columns breaking 175px down as follows: 50px-50px-75px
Col02
Col2 = colspan 3 over 100px
3 Sub-columns breaking 100px down as follows: 40px-30px-30px
Col03
Col3 = colspan 1 over 100px
No sub-columns
Col04
Col4 = colspan 5 over 150px
5 sub-columns breaking 150px down as follows: 30px-30px-30px-30px-30px
Col05
Col5 = colspan 1 over 100px
No sub-columns
Edit: Fix for fixed position on window resize:
http://jsfiddle.net/eReBn/13/
Complete Code:
$(function() {
(function($) {
$.fn.fTable = function(o) {
var tableTmpl = '<table id="XYZ_fixed" border="1" width="625" cellspacing="0" cellpadding="0" align="center" class="base"></table>';
this.wrap('<div class="fTable_container" />');
var fc = this.parent();
fc.css('width', this.width() + 18);
this.wrap('<div class="fTable_rContainer" />');
var rc = this.parent();
rc.css('height', o.height);
var fTable = $(tableTmpl);
fTable
.addClass('fTable_fixedHead')
.html(this.find('thead').clone())
.prependTo(rc);
$(window).on('scroll resize', function () {
console.log(isScroll());
if (isScroll()) {
fTable.css('left', $(this).scrollLeft() * -1);
} else {
fTable.css('left', '');
}
});
};
function isScroll() {
var root= document.compatMode=='BackCompat'?
document.body : document.documentElement;
return root.scrollWidth>root.clientWidth;
}
})(jQuery);
$('#XYZ').fTable({
height: 300
});
});
Edit: Try below solution.. since you were ok to manually add styles and adjust table.
DEMO: http://jsfiddle.net/eReBn/12/embedded/result/
Manual:
a. Copy below CSS to your style sheet
.fTable_rContainer {
position: relative;
overflow: auto;
height: 300px; /* Height of the table */
}
.fTable_container {
width: 643px; /* Total width of the table you set + 18px (scroll size) */
}
.fTable_fixedHead {
position: fixed;
}
b. Wrap the Table with div class="fTable_container"
c. Copy the thead part of the original table and move to a new table as in DEMO.
d. Add class fTable_fixedHead to the new table
Automated with some scripts:
DEMO: http://jsfiddle.net/eReBn/11/embedded/result/
I tried to solve it using 2 tables,
Original table (untouched)
Fixed Header table(absolute positioned) over the original table
Check it out and let me know if you like it.
DEMO
Tested in Firefox & Chrome. [Will test on other browsers later today]
Full code # http://jsfiddle.net/eReBn/7/
JS:
$(function() {
(function($) {
$.fn.fTable = function(o) {
/* Preserve the attr list from original table */
var el = this[0], arr = [], it;
for (var i = 0, attrs = el.attributes, l = attrs.length; i < l; i++) {
it = attrs.item(i);
if (it.nodeName == 'id') {
arr.push(it.nodeName + '="' + it.nodeValue + '_fixed"');
} else {
arr.push(it.nodeName + '="' + it.nodeValue + '"');
}
}
var tableTmpl = '<table ' + arr.join(' ') + '></table>';
/* Wrap it inside div's */
this.wrap('<div class="fTable_container" />');
this.wrap('<div class="fTable_rContainer" />');
var rc = this.parent();
/* Clone the thead and add it to the fixed table head */
$(tableTmpl)
.addClass('fTable_fixedHead')
.html(this.find('thead').clone())
.prependTo(rc);
/* Position the fixed head table on scroll */
rc.scroll(function() {
rc.find('.fTable_fixedHead').css('top', $(this).scrollTop());
});
var _that = this;
rc.find('.fTable_fixedHead').css('left', _that.aPosition().left);
/* Position the left on resize*/
$(window).resize(function() {
rc.find('.fTable_fixedHead').css('left', _that.aPosition().left);
});
};
/* Position fix for webkit browsers */
jQuery.fn.aPosition = function() {
thisLeft = this.offset().left;
thisTop = this.offset().top;
thisParent = this.parent();
parentLeft = thisParent.offset().left;
parentTop = thisParent.offset().top;
return {
left: thisLeft - parentLeft,
top: thisTop - parentTop
}
}
})(jQuery);
});
Usage:
$('#XYZ').fTable({
height: 300
});
We used DataTables.net and solved a very similar problem quite nicely. Here is an example from their website with an advanced multi-row header. You can also sort within your sub-column categories.
I may as well provide an option for you. This assumes the browser window is being scrolled.
The idea here is that a clone of your table is made and everything but the thead is stripped out of it. It is fixed to the top of the viewport and hidden.
When the user scrolls past the top of the table, the cloned table is displayed. I have also included a resize event to handle changes to the viewport's width. There are some minor hiccups with keeping the fixed header and the table aligned horizontally, but it's not a big issue.
Here is a demo using your table as source (I moved a lot of the inline styles to CSS just to make things easier to read):
jsFiddle DEMO
$(function(){
var $window = $(window),
stickyTable = $('#XYZ'),
stickyHeader = stickyTable.clone(true),
tableTop = stickyTable.offset().top,
isSticky = false;
handleScroll();
$window.on({
scroll: handleScroll,
resize: handleResize
});
stickyHeader.find('tbody').remove();
stickyHeader.find('tfoot').remove();
stickyHeader.addClass('sticky-header').appendTo('body');
function handleScroll() {
var scrollTop = $window.scrollTop();
if(scrollTop > tableTop && !isSticky) {
stickyHeader.css('left',stickyTable.offset().left+'px').show();
isSticky = true;
} else if(scrollTop <= tableTop && isSticky) {
stickyHeader.hide();
isSticky = false;
}
}
function handleResize() {
if(isSticky) {
stickyHeader.css('left',stickyTable.offset().left+'px');
}
}
});
Some of the previously written answers were brilliant!!
I tested this in Google Chrome most of the time, due to the brilliant Chrome Developer tools for debugging Javascript on the fly, and tweaking css in realtime.
If you added jQuery with a clone function in it, you could achieve a scrollable table without squashing it into a page height, and have a floating Table header wherever you want it
The following code will clone the table header:
jQuery(document).ready(function () {
jQuery("body").append("<table class='tableheader' align='center' border='1' width='625' cellspacing='0' cellpadding='0'></table>");
jQuery(".tableheader").append(jQuery(".base2 thead").clone());
Then you need to set some static variable, and catch the scrolling event:
var headerToMove = jQuery(".tableheader");
var headerOffsetTop = headerToMove.offset().top;
var headerPosition = headerToMove.position();
jQuery(window).scroll(function () { scroll_post_header(); });
Then move the header up and down over the current header, but keep it on top of the page, if the current header scrolls out of the way.
function scroll_post_header() {
var new_position = headerOffsetTop - jQuery(window).scrollTop();
if (new_position < 0) { new_position = 0;}
if (headerPosition.top != new_position) {
headerToMove.css("top",new_position);
//headerToMove.stop().animate({ 'top': new_position }, 300);
}
if (jQuery(window).scrollTop() < 15) { headerToMove.css("top",headerOffsetTop);}
}
});
Now ad some initial CSS to the header that will be cloned. You will need to find that sweet spot where your current header is from the top, but even this can be achieved via jQuery interrogation.
.tableheader {
position: fixed;
height: 80px;
top: 160px;
left: 2.5%;
z-index: 1000;
}
The full working example can be found at: http://jsfiddle.net/webwarrior/YZ8cJ/142/
Hope this helps somewhat without changing any html.
If you .clone() the thead and put it in the table all of the columns retain their original width.
Update: I realized that IE 6 and 7 were having problems and I fixed them. The issue was that those versions of IE don't allow you to position the thead directly. As a result, I changed it so that it moves the thead tr instead, but only if the browser is IE < 8.
Another bug was that IE < 9 had problems with .prependTo(). I'm not sure if it is just IE Tester or not, but I resolved the issue by using .after().
Updated Demo
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>
// http://obvcode.blogspot.com/2007/11/easiest-way-to-check-ie-version-with.html
var Browser = {
version: function() {
var version = 999; // we assume a sane browser
if (navigator.appVersion.indexOf("MSIE") != -1) {
// bah, IE again, lets downgrade version number
version = parseFloat(navigator.appVersion.split("MSIE")[1]);
}
return version;
}
}
var $thead = $('#XYZ thead'),
$new_thead = $thead.clone().hide(),
$window = $(window),
distance_from_top = $thead.offset().top,
did_scroll = false; // http://ejohn.org/blog/learning-from-twitter/
// for IE <= 7
var $tr_1, $tr_2;
// add the cloned thead
$thead.after($new_thead);
if( Browser.version() < 8 ) {
$new_thead.find('tr').css({
'position': 'absolute',
'top': 0,
'margin-left': -1
});
$tr_1 = $new_thead.find('tr:first');
$tr_2 = $new_thead.find('tr:last').css('top', $tr_1.height());
}else {
$new_thead.css({
'position': 'fixed',
'width': $thead.width(),
'top': 0
});
}
$window.scroll(function() {
if( Browser.version() < 8 ) {
did_scroll = true;
}
if( $window.scrollTop() >= distance_from_top ) {
$new_thead.show();
}else {
$new_thead.hide();
}
});
setInterval(function() {
if( did_scroll ) {
did_scroll = false;
$tr_1.css('top', $window.scrollTop());
$tr_2.css('top', $tr_1.height() + $window.scrollTop());
}
}, 250);
</script>
Edit
I didn't realize that K.M. wanted a fixed height table. If you look at my example above you'll see that the table header is fixed if the browser scrolls past the top of the table. However, from my understanding this is not what he/she wanted.
I have tested this in the following browsers on my Mac.
Safari (5.1.7)
Firefox (11.0)
Chrome (21.0.1180.75)
And I have tested this in Windows 7 with:
IE 8 and 9 using IE Tester
Firefox (5.0.1, 6.0.2, 7.0.1, 8.0.1, 10.0.2)
Chrome (12.0.742.91)
Safari (5.1.5)
It is broken in IE 6 and 7, but I didn't look into seeing if there was a fix.
Demo
var table = $('#XYZ'),
thead = table.find('thead'),
fixed_thead = thead.clone(),
// create a copy of the original table
fixed_thead_wrapper = $('<table />', {
'id': 'fixed_thead_wrapper',
'align': 'center',
'width': table.outerWidth(),
'border': '1',
'cellspacing': 0,
'cellpadding': 0
}).insertBefore(table),
// this forces the table to be in a scrollable area
table_wrapper = $('<div />', {
'id': 'fixed_table_wrapper',
'height': 300,
css: {
'overflow': 'auto'
}
});
// add the cloned thead to the new table
fixed_thead_wrapper.append(fixed_thead);
// hide the original thead.
// this is a very hackish way of doing this, but I'm not sure of a better way as of right now
table.css({
'position': 'relative',
'top': fixed_thead_wrapper.height() * -1
});
// wrap the original table
table.wrap(table_wrapper);
// line the tables up now that the scrollbar is present
fixed_thead_wrapper.css({
'position': 'relative',
'left': table.offset().left - fixed_thead_wrapper.offset().left
});
I've always used this super easy to use Plugin that handles it as easily as $('#tableID').fixedtableheader();
Works amazingly, very lightweight and flexible.
http://fixedheadertable.com/
Check out HTML table with fixed headers?. It is the best answer to the general question "how can I freeze a table header?"
Check this Fiddle
Here is the Code. Working fine in all browsers...!
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Scrollable Table with Fixed Header</title>
<meta http-equiv="language" content="en-us">
<script type="text/javascript">
function removeClassName (elem, className) {
elem.className = elem.className.replace(className, "").trim();
}
function addCSSClass (elem, className) {
removeClassName (elem, className);
elem.className = (elem.className + " " + className).trim();
}
String.prototype.trim = function() {
return this.replace( /^\s+|\s+$/, "" );
}
function stripedTable() {
if (document.getElementById && document.getElementsByTagName) {
var allTables = document.getElementsByTagName('table');
if (!allTables) { return; }
for (var i = 0; i < allTables.length; i++) {
if (allTables[i].className.match(/[\w\s ]*scrollTable[\w\s ]*/)) {
var trs = allTables[i].getElementsByTagName("tr");
for (var j = 0; j < trs.length; j++) {
removeClassName(trs[j], 'alternateRow');
addCSSClass(trs[j], 'normalRow');
}
for (var k = 0; k < trs.length; k += 2) {
removeClassName(trs[k], 'normalRow');
addCSSClass(trs[k], 'alternateRow');
}
}
}
}
}
window.onload = function() { stripedTable(); }
</script>
<style type="text/css">
body {
background: #FFF;
color: #000;
font: normal normal 12px Verdana, Geneva, Arial, Helvetica, sans-serif;
margin: 10px;
padding: 0
}
table, td, a {
color: #000;
font: normal normal 12px Verdana, Geneva, Arial, Helvetica, sans-serif
}
h1 {
font: normal normal 18px Verdana, Geneva, Arial, Helvetica, sans-serif;
margin: 0 0 5px 0
}
h2 {
font: normal normal 16px Verdana, Geneva, Arial, Helvetica, sans-serif;
margin: 0 0 5px 0
}
h3 {
font: normal normal 13px Verdana, Geneva, Arial, Helvetica, sans-serif;
color: #008000;
margin: 0 0 15px 0
}
div.tableContainer {
clear: both;
border: 1px solid #963;
height: 285px;
overflow: auto;
width: 756px
}
html>body div.tableContainer {
overflow: hidden;
width: 756px
}
div.tableContainer table {
float: left;
width: 740px
}
html>body div.tableContainer table {
width: 756px
}
thead.fixedHeader tr {
position: relative
}
html>body thead.fixedHeader tr {
display: block
}
thead.fixedHeader th {
background: #C96;
border-left: 1px solid #EB8;
border-right: 1px solid #B74;
border-top: 1px solid #EB8;
font-weight: normal;
padding: 4px 3px;
text-align: left
}
thead.fixedHeader a, thead.fixedHeader a:link, thead.fixedHeader a:visited {
color: #FFF;
display: block;
text-decoration: none;
width: 100%
}
thead.fixedHeader a:hover {
color: #FFF;
display: block;
text-decoration: underline;
width: 100%
}
html>body tbody.scrollContent {
display: block;
height: 262px;
overflow: auto;
width: 100%
}
tbody.scrollContent td, tbody.scrollContent tr.normalRow td {
background: #FFF;
border-bottom: none;
border-left: none;
border-right: 1px solid #CCC;
border-top: 1px solid #DDD;
padding: 2px 3px 3px 4px
}
tbody.scrollContent tr.alternateRow td {
background: #EEE;
border-bottom: none;
border-left: none;
border-right: 1px solid #CCC;
border-top: 1px solid #DDD;
padding: 2px 3px 3px 4px
}
html>body thead.fixedHeader th {
width: 200px
}
html>body thead.fixedHeader th + th {
width: 240px
}
html>body thead.fixedHeader th + th + th {
width: 316px
}
html>body tbody.scrollContent td {
width: 200px
}
html>body tbody.scrollContent td + td {
width: 240px
}
html>body tbody.scrollContent td + td + td {
width: 300px
}
</style>
</head><body>
<div id="tableContainer" class="tableContainer">
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="scrollTable">
<thead class="fixedHeader">
<tr class="alternateRow">
<th>Header 1</th>
<th>Header 2</th>
<th>Header 3</th>
</tr>
</thead>
<tbody class="scrollContent">
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>End of Cell Content 1</td>
<td>End of Cell Content 2</td>
<td>End of Cell Content 3</td>
</tr>
</tbody>
</table>
</div>
</body></html>
I have an idea for you. You will split your table into two tables. One of them will acting as the header and the second will acting as the rest of all cells and rows.
The trick in the following:
Placing two fixed width and height divs vertical adjacent to themselves. the upper one will loads the header table and the n the lower will acts for the table body. in the lower div you will set vertical overflow. By this way you will have simple solution's that supports your header move with data scrolling.
It seems the best way to go about this is two tables. Outer table with headers, and one big row with an inner table inside that. The div should be css'ed overflow: scroll or auto with a set height (via css or javascript). You will have to set specific widths on all the columns. I think the best way to go about that is giving a class for each header, and make sure the corresponding columns in the inner table have the same classes.
<table>
<tr><th>...</th></tr>
<tr><td>
<div><inner table></div>
</td></tr>
</table>
Check this out for examples: http://www.cssplay.co.uk/menu/tablescroll.html
Very quick and sketchy, but hopefully you get the idea, why not just use position:fixed for the thead.
jsfiddle: http://jsfiddle.net/b3S5F/