Well as mentioned in the Firefox 4 changelog, there will be no longer support for scrollable <tbody>
's.
There are a bunch of workarounds - javascript or 2 seperate tables - but none of them solves all problems. Javascript is obviously slower (with 600 rows you can forget to try to scroll a table), and 2 tables are gonna be problematic with cell-width.
Do you know if there is some cool way to do this? We are using jsf / facelets and now have to redo the tags, starting with a good idea would be awesome :-)
I know you're trying to avoid js/separate table implementations, but it was the only one I could find that worked across multiple browsers. Try http://www.tablefixedheader.com/. It's a jquery solution and worked, in my limited testing, across IE6 / IE8 / FF3. (Haven't tested FF4).
PrimeFaces and RichFaces have scrollable datatable components which fetches new rows by ajax.
(both JSF component libraries uses jQuery under the covers anyway)
Thanks to digitaldigs from MozillaZine, I was able to make it work with few modifications.
The fix works great for FF4, FF7 and FF11.
I hope this helps! :)
Issues I have fixed-
1. scrollWidth did not work for me so had to go for offsetWidth
Scroll bar width 16 pixel also was not helping so removed it. Instead made my tbody style as-
.scrollContent {
overflow-x:hidden;
overflow-y:scroll; /* I did this only for Mozilla. IE will show 2 scroll bars if applied*/
display:block;
}
With change #2, I had to pad the last cell of the header to accomodate scroll bar.
/* I did this only for Mozilla. Not for IE*/
.fixedHeader tr th:last-child {
padding-right: 20px;
}
My fixed header table has colspan used a lot hence was not able to set width, so I first look for a correct row with correct number of cells and then I process.
My code looks like below:
function makeMeFixedHeader(){
var tbodys = document.getElementsByName("scrollTableBody");
if (tbodys){
for(var i=0;i<tbodys.length;i++){
// We can put logic here to check if the current height
// of tbody has crossed the specified limit or not
do_tbodyscroll(tbodys[i]);
}
}
}
function do_tbodyscroll(_tbody){
// get the table node
var table = _tbody.parentNode;
// Get the Last row in Thead ...
// COLGROUPS USING COLSPAN NOT YET SUPPORTED
var thead = table.getElementsByTagName("THEAD")[0];
var _rows = thead.getElementsByTagName("TR");
var tableheader = _rows[_rows.length - 1];
var headercells = tableheader.cells;
// rows of tbody
var _frows = _tbody.getElementsByTagName("TR");
// first row of tbody
var _fr = _tbody.getElementsByTagName("TR")[0];
//var _fr = _tbody.getElementsByName("scrollTableRow")[0];
// first row cells ..
var _frcells = _fr.cells;
if (_frcells.length < headercells.length){
var rowCount = 1;
while (rowCount < _frows.length){
// nth row of tbody
_fr = _tbody.getElementsByTagName("TR")[rowCount];
//var _fr = _tbody.getElementsByName("scrollTableRow")[rowCount];
// nth row cells ..
_frcells = _fr.cells;
if (headercells.length == _frcells.length){
break;
}
rowCount++;
}
}
// Apply width to header ..
for(var i=0; i<headercells.length; i++){
if (tableheader.cells[i].offsetWidth != _fr.cells[i].offsetWidth){
var lastColumn = (i == headercells.length-1)?true:false;
var changeWidth = (lastColumn)? ((rowCount >= 1)?true:false)
:true;
var headerW = tableheader.cells[i].offsetWidth;
var cellW = _fr.cells[i].offsetWidth;
if (headerW < cellW){
tableheader.cells[i].width = cellW;
_fr.cells[i].width = tableheader.cells[i].width;
if (lastColumn)
tableheader.cells[i].width = tableheader.cells[i].offsetWidth-20;
}else{
tableheader.cells[i].width = headerW;
_fr.cells[i].width = tableheader.cells[i].width;
if (lastColumn)
_fr.cells[i].width = tableheader.cells[i].offsetWidth-20;
}
}
}
//var j = headercells.length-1;
// ADD 16 Pixel of scroll bar to last column ..
//tableheader.cells[j].width = _fr.cells[j].offsetWidth + 20;
tableheader.style.display = "block";
_tbody.style.display = "block";
}
Just got slammed with this after I upgraded. Boo.
I found this in the tubes.
This code is working in FFX 4. Not in IE8... could be modified.
http://www.imaputz.com/cssStuff/bigFourVersion.html
Try the first method of this page, pure CSS with a single table (2 divs around the table, and the thead is positionned absolute) : tablescroll
Seems to work on FF4/IE9/IE8 in addition to IE7/FF3.6.
Why not use a scrollable div? Maybe one of these two options:
<table>
<row>
<cell>
<div of headers>
<div of content (scrollable)>
</cell>
</row>
</table>
or simpler:
<div of headers>
<div (scrollable)>
<table of content, no headers>
</div>
Not pretty I know but hey I'm no Michaelangelo :)
I worked on a frameworkless prototype I want to share with you.
It's working in Firefox 4+, IE7-8 (haven't checked 6 or 9) Safari and Chrome.
The biggest unsolved problem is what to do when the static headers run out of room to shrink before the data columns do.
For the actual project I was working on frankly I backed out of the scrolling tbody and went with pagination. but I was dealing with 25+ columns with sorting and potentially 1000s of rows.
Anyway this is where I got to before I abandoned it. Please post your additions if you improve it:
Hope this helps.
Link to an example
<!DOCTYPE html>
<html>
<head>
<title>Table with Fixed Header Prototype</title>
<script type="text/javascript">
</script>
<style type="text/css">
.tableContainer{
background:#eee;
width:80%;
}
.scrollContainer{
width:100%;
height:500px; /** The only thing required */
}
.staticHeaderTable{
-moz-box-shadow: 1px 4px 5px #cccccc;
-webkit-box-shadow:1px 4px 5px #cccccc;
box-shadow: 1px 4px 5px #cccccc;
position: relative;
z-index: 10;
}
/*****************************************************/
/**** Generic styles outside of problem scope ****/
table{border-collapse: collapse;font:normal 12px sans-serif;}
th,td{padding:2px; height:20px;border:1px solid #dddddd;}
th{background:#ffcc00;}
</style>
</head>
<body>
<script>
function makeStaticTableHeaders(tableId){
// Overall Container : wraps everything
var tableContainer = document.createElement("div");
tableContainer.className = "tableContainer";
// Scrolling Container : must have a height.(Set in CSSS in this sample) this contains the table without the head or foot
var scrollContainer = document.createElement("div");
scrollContainer.className = "scrollContainer";
scrollContainer.style.overflowY = "auto"; // just Y since IE7 doesn't add the scrollbar to the width.
scrollContainer.style.overflowX = "hidden"; // IE7 override. consider a CSS fix
scrollContainer.style.width = "100%";
// Identifies the actual table to wrap from the dom. exits if it can't be found
var dataTable = document.getElementById(tableId);
if(typeof(dataTable) == "undefined"){
return false;
}
dataTable.style.width = "100%";
// Identify the header. If there is none, there is no point in this so exit.
var header = dataTable.getElementsByTagName("thead");
if (header.length == 0){
return false;
}
for (var i = 0; i < header.length; i++)
{
if(header[i].className.indexOf("static") != -1){
header = header[i];
break;
}
if(i == header.length - 1){
header = header[i];// failsafe
}
}
// If we are still here, we begin the process of altering the DOM elements
// 1. Insert the tableContainer in front of the dataTable
dataTable.parentNode.insertBefore(tableContainer, dataTable);
// 2. Insert the scrollContainer into the table container
tableContainer.appendChild(scrollContainer);
// 3. get the thead tr and create staticHeaderTable with the tr
var headerRow = header.getElementsByTagName("tr")[header.getElementsByTagName("tr").length - 1]; // gets last tr in case there is more than one
var staticHeaderTable = document.createElement("table");
staticHeaderTable.className = "staticHeaderTable";
staticHeaderTable.appendChild(header);
staticHeaderTable.style.width = "100%";
// 4. Put the staticHeaderTable in the scrollContanier
tableContainer.insertBefore(staticHeaderTable, scrollContainer);
// 5. take the datatable out of the dom and put it in the scrollContainer
scrollContainer.appendChild(dataTable);
// 6. footer(optional)
var footer = dataTable.getElementsByTagName("tfoot");
if (footer.length > 0){
for (var i = 0; i < footer.length; i++)
{
if(footer[i].className.indexOf("static") != -1){
footer = footer[i];
break;
}
if(i == footer.length - 1){
footer = footer[i];// failsafe
}
}
// TODO: footer assumes columns are not linked to the data columns.
var staticFooterTable = document.createElement("table");
staticFooterTable.className = "staticFooterTable";
staticFooterTable.appendChild(footer);
staticFooterTable.style.width = "100%";
tableContainer.appendChild(staticFooterTable);
}
function tableResize(){
var firsttableRow = dataTable.getElementsByTagName("tbody")[0].getElementsByTagName("tr")[0];
var extra = headerRow.offsetWidth - firsttableRow.offsetWidth;
var headerCells = (headerRow.getElementsByTagName("td").length > 0 )? headerRow.getElementsByTagName("td") : headerRow.getElementsByTagName("th");
for(var i=0; i <headerCells.length;i++){
headerCells[i].style.width = firsttableRow.getElementsByTagName("td")[i].offsetWidth + ((i==headerCells.length-1)? extra: 0) + "px";
}
};
if(window.addEventListener) {window.addEventListener("resize", tableResize, false);}
else{window.attachEvent("onresize", tableResize);}
tableResize();
}
window.onload = function(){
var staticTable = makeStaticTableHeaders("dataTable");
}
</script>
<h2>Datatable</h2>
<table id="dataTable" class="dataTable">
<thead class="static">
<tr>
<th>Header 1 A long One</th>
<th>Header 2</th>
<th>Header 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cell Content 1 All the good Stuff is in here</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr>
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr>
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr>
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr>
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr>
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr>
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr>
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr>
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr>
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr>
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr>
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr>
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr>
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr>
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr>
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr>
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr>
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr>
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr>
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr>
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr>
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr>
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr>
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr>
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr>
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr>
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr>
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr>
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr>
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr>
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr>
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr>
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr>
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr>
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr>
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr>
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr>
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr>
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr>
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr>
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr>
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr>
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr>
<td>End of Cell Content 1</td>
<td>End of Cell Content 2</td>
<td>End of Cell Content 3</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="3">Footer Is Here</td>
</tr>
</tfoot>
</table>
</body>
</html>