|
I have been working on Scrollable tables from past 2 years. I have implemented fix header table using CSS and JavaScript.
Scrollable Table in IE
Scrollable in All
Please let me know if you have any suggestions for me
Regards,
Shahib
|
|
|
|
|
nice creation...what i need might be solved
|
|
|
|
|
Hi,
I have made a couple of changes to make this excelent piece of code support Firefox and Chrome.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Fixed Headers</title>
<style type="text/css">
body, td, p {font-family: Verdana;font-size: 10pt;}
h1{font-family: Verdana;font-size: 14pt;}
table{border-collapse: collapse;}
td, th{border-top: solid 1px #666666;border-bottom: solid 1px #666666;white-space: nowrap;padding-left: 10px;padding-right: 10px;text-align: left;}
th{background-color: #666666;color: #ffffff;}
#outerDiv{position: relative;}
#innerDiv{overflow: auto;}
#innerDiv td{white-space: nowrap;}
</style>
<script type="text/javascript">
var divContent = null;
var divTbl = null;
var divHeaderRow = null;
var divHeaderColumn = null;
var divHeaderRowColumn = null;
var headerRowFirstColumn = null;
var x;
var y;
var horizontal = false;
var vertical = false;
// Code inspiration "from http://stackoverflow.com/questions/920478/javascript-traversing-the-html-dom-using-childnodes-causes-errors-in-non-ie-bro"
function child(elem, index) {
// if index is not supplied, default is 1
// you might be more comfortable making this 0-based
// in which case change i initial assignment value to 0 too
index = index || 1;
// get first child element node of elem
elem = (elem.firstChild && elem.firstChild.nodeType != 1) ?
next(elem.firstChild) :
elem.firstChild;
// use the index to move to nth-child element node
for(var i=1; i < index;i++) {
(function() {
return elem = next(elem);
})();
}
return elem;
}
function next(elem) {
do {
elem = elem.nextSibling;
} while (elem && elem.nodeType != 1);
return elem;
}
// end inspiration
// Copy table to top and to left
function CreateScrollHeader(content, scrollHorizontal, scrollVertical)
{
horizontal = scrollHorizontal;
vertical = scrollVertical;
if (content != null)
{
divContent = content;
divTbl=child(divContent);
var headerRow = child(child(divTbl));
x = divTbl.offsetWidth;
y = divTbl.offsetHeight;
divHeaderRow = divContent.cloneNode(true);
if (horizontal)
{
divHeaderRow.style.height = headerRow.offsetHeight + "px";
divHeaderRow.style.overflow = "hidden";
divContent.parentNode.insertBefore(divHeaderRow, divContent);
divTbl.style.position = "absolute";
divTbl.style.top = "-" + headerRow.offsetHeight + "px";
y = y - headerRow.offsetHeight;
}
divHeaderRowColumn = divHeaderRow.cloneNode(true);
headerRowFirstColumn = child(headerRow);
divHeaderColumn = divContent.cloneNode(true);
divContent.style.position = "relative";
if (vertical)
{
divContent.parentNode.insertBefore(divHeaderColumn, divContent);
divContent.style.left = headerRowFirstColumn.offsetWidth + "px";
divTbl.style.position = "absolute";
divTbl.style.left = "-" + headerRowFirstColumn.offsetWidth + "px";
}
else
{
divContent.style.left = 0 + "px";
}
if (vertical)
{
divHeaderColumn.style.width = headerRowFirstColumn.offsetWidth + "px";
divHeaderColumn.style.overflow = "hidden";
divHeaderColumn.style.zIndex = "99";
divHeaderColumn.style.position = "absolute";
divHeaderColumn.style.left = "0" + "px";
addScrollSynchronization(divHeaderColumn, divContent, "vertical");
x = x - headerRowFirstColumn.offsetWidth;
}
if (horizontal)
{
if (vertical)
{
divContent.parentNode.insertBefore(divHeaderRowColumn, divContent);
}
divHeaderRowColumn.style.position = "absolute";
divHeaderRowColumn.style.left = "0" + "px";
divHeaderRowColumn.style.top = "0" + "px";
divHeaderRowColumn.style.width = headerRowFirstColumn.offsetWidth + "px";
divHeaderRowColumn.overflow = "hidden";
divHeaderRowColumn.style.zIndex = "100";
divHeaderRowColumn.style.backgroundColor = "#ffffff";
}
if (horizontal)
{ addScrollSynchronization(divHeaderRow, divContent, "horizontal");
}
if (horizontal || vertical)
{
window.onresize = ResizeScrollArea;
ResizeScrollArea();
}
}
}
// Resize scroll area to window size.
function ResizeScrollArea()
{ var height = document.documentElement.clientHeight - 120;
if (!vertical)
{
height -= divHeaderRow.offsetHeight;
}
var width = document.documentElement.clientWidth - 50;
if (!horizontal)
{
width -= divHeaderColumn.offsetWidth;
}
var headerRowsWidth = 0;
divTbl.style.width = x + "px";
divTbl.style.height = y + "px";
if (divHeaderRowColumn != null)
{
headerRowsWidth = divHeaderRowColumn.offsetWidth;
}
// width
if (divTbl.offsetWidth > width)
{
divContent.style.width = Math.max(width - headerRowsWidth, 0) + "px";
divContent.style.overflowX = "scroll";
divContent.style.overflowY = "auto";
}
else
{
divContent.style.width = x + "px";
divContent.style.overflowX = "auto";
divContent.style.overflowY = "auto";
}
if (divHeaderRow != null)
{
divHeaderRow.style.width = divContent.offsetWidth + headerRowsWidth + "px";
}
// height
if (divTbl.offsetHeight > height)
{
divContent.style.height = Math.max(height, 80) + "px";
divContent.style.overflowY = "scroll";
}
else
{
divContent.style.height = y + "px";
divContent.style.overflowY = "hidden";
}
if (divHeaderColumn != null)
{
divHeaderColumn.style.height = divContent.offsetHeight + "px";
}
// check scrollbars
if (divContent.style.overflowY == "scroll")
{
divContent.style.width = divContent.offsetWidth + 17 + "px";
}
if (divContent.style.overflowX == "scroll")
{
divContent.style.height = divContent.offsetHeight + 17 + "px";
}
// divContent.parentElement.style.width = divContent.offsetWidth + headerRowsWidth;
divContent.parentNode.style.width = divContent.offsetWidth + headerRowsWidth + "px";
}
// ********************************************************************************
// Synchronize div elements when scrolling
// from http://webfx.eae.net/dhtml/syncscroll/syncscroll.html
// ********************************************************************************
// This is a function that returns a function that is used
// in the event listener
function getOnScrollFunction(oElement) {
return function () {
if (oElement._scrollSyncDirection == "horizontal" || oElement._scrollSyncDirection == "both")
oElement.scrollLeft = event.srcElement.scrollLeft;
if (oElement._scrollSyncDirection == "vertical" || oElement._scrollSyncDirection == "both")
oElement.scrollTop = event.srcElement.scrollTop;
};
}
// This function adds scroll syncronization for the fromElement to the toElement
// this means that the fromElement will be updated when the toElement is scrolled
function addScrollSynchronization(fromElement, toElement, direction) {
removeScrollSynchronization(fromElement);
fromElement._syncScroll = getOnScrollFunction(fromElement);
fromElement._scrollSyncDirection = direction;
fromElement._syncTo = toElement;
if(window.addEventListener)
{ toElement.addEventListener("onscroll", fromElement._syncScroll, false);
}
else
toElement.attachEvent("onscroll", fromElement._syncScroll);
}
// removes the scroll synchronization for an element
function removeScrollSynchronization(fromElement) {
if (fromElement._syncTo != null)
{ if(window.addEventListener)
{ toElement.addEventListener("onscroll", fromElement._syncScroll, false);
}
else
fromElement._syncTo.detachEvent("onscroll", fromElement._syncScroll);
}
fromElement._syncTo = null;
fromElement._syncScroll = null;
fromElement._scrollSyncDirection = null;
}
</script>
</head>
<body>
<h1>Fixed Headers</h1>
<div id="outerDiv">
<div id="innerDiv">
<table>
<tr id="HeaderRow">
<th>Table</th>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
<th>Column 4</th>
<th>Column 5</th>
<th>Column 6</th>
<th>Column 7</th>
<th>Column 8</th>
<th>Column 9</th>
<th>Column 10</th>
<th>Column 11</th>
<th>Column 12</th>
</tr>
<tr>
<th>Row 1</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 2</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 3</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 4</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 5</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 6</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 7</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 8</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 9</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 10</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 11</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 12</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 13</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 14</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 15</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 16</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 17</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 18</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 19</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
<tr>
<th>Row 20</th>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
</table>
</div>
</div>
</body>
</html>
<script language="javascript">
CreateScrollHeader(document.getElementById("innerDiv"), true, true);
</script>
Kind Regards
Arne Møller
|
|
|
|
|
Hi.
This mostly works - except the first column and top row don't scroll when the table is scrolled.
modified on Friday, July 9, 2010 11:40 AM
|
|
|
|
|
Hi
I've made a version of the script that supports freezing multiple columns.
The HTML should look like this:
<pre>
<div id="outerDiv">
<div id="innerDiv">
<table>
<thead>
<tr>
<th>Table</th>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
<th>Column 4</th>
<th>Column 5</th>
<th>Column 6</th>
<th>Column 7</th>
<th>Column 8</th>
<th>Column 9</th>
<th>Column 10</th>
<th>Column 11</th>
<th>Column 12</th>
</tr>
</thead>
<tbody>
<tr>
<th>Row 1</th>
<th>Value 1</th>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
<td>Value 5</td>
<td>Value 6</td>
<td>Value 7</td>
<td>Value 8</td>
<td>Value 9</td>
<td>Value 10</td>
<td>Value 11</td>
<td>Value 12</td>
</tr>
...
</pre>
All cells wrapped in 'th' will be fixed.
The script (Based on Kenneths version):
<pre>
var divContent = null;
var divHeaderRow = null;
var divHeaderColumn = null;
var divHeaderRowColumn = null;
var fixedColumnWidth;
var x;
var y;
var horizontal = false;
var vertical = false;
// Copy table to top and to left
function CreateScrollHeader(content, scrollHorizontal, scrollVertical)
{
horizontal = scrollHorizontal;
vertical = scrollVertical;
divContent = content;
if(BrowserDetect.browser == "Konqueror") {
window.onload = CreateScrollHeaderActual;
} else {
CreateScrollHeaderActual();
}
}
function CreateScrollHeaderActual() {
if (divContent != null)
{
var widthCosmeticAdd = 0;
var heightCosmeticAdd = 0;
// browser specific cosmetic tweaks
if (BrowserDetect.browser == "Explorer") {
widthCosmeticAdd = 2;
heightCosmeticAdd = 2;
} else if(BrowserDetect.browser == "Opera" || BrowserDetect.browser == "Konqueror" || BrowserDetect.browser == "Safari") {
widthCosmeticAdd = 1;
heightCosmeticAdd = 1;
}
var originalTable = findFirstElementByType(divContent, 'table');
var headerRow = findFirstElementByType(originalTable, 'thead');
//Find the no of fixed colums
var firstRow = findFirstElementByType(findFirstElementByType(originalTable, 'tbody'),'tr');
var noOfFixedCols = firstRow.getElementsByTagName('th').length;
//Calculate the total width of the fixed columns
var headerRowTR = findFirstElementByType(headerRow,'tr');
fixedColumnWidth = 0;
for( i = 0 ; i < noOfFixedCols ; i++ ) {
fixedColumnWidth = fixedColumnWidth + headerRow.getElementsByTagName('th')[i].offsetWidth;
}
x = originalTable.offsetWidth;
y = originalTable.offsetHeight;
//Clone the entire table - including innerDiv
divHeaderRow = divContent.cloneNode(true);
//Should the header row remain fixed?
if (horizontal)
{
//Set the height of the innerDiv div
divHeaderRow.style.height = (headerRow.offsetHeight + heightCosmeticAdd) + 'px';
//Make sure no scrollbars are visible in the new header row
divHeaderRow.style.overflow = "hidden";
//Insert the copy of innerDiv before the existing innerDiv
divContent.parentNode.insertBefore(divHeaderRow, divContent);
//Set position type to absolute - in order to subtract the 'new' header row
originalTable.style.position = "absolute";
//Make sure the header row is not visible on the original table
originalTable.style.top = "-" + (headerRow.offsetHeight + heightCosmeticAdd) + 'px';
y = y - headerRow.offsetHeight;
}
//Clone the div just created abowe
divHeaderRowColumn = divHeaderRow.cloneNode(true);
//Clone the original innerDiv
divHeaderColumn = divContent.cloneNode(true);
divContent.style.position = "relative";
if (vertical)
{
//Insert the fixed column before orginal table
divContent.parentNode.insertBefore(divHeaderColumn, divContent);
//Push the the orginal innerDiv right
divContent.style.left = fixedColumnWidth + 'px';
originalTable.style.position = "absolute";
//Make sure the first column isn't visiable in the orginal table
originalTable.style.left = "-" + fixedColumnWidth + 'px';
}
else
{
divContent.style.left = "0px";
}
if (vertical)
{
//Set the width of the fixed column
divHeaderColumn.style.width = (fixedColumnWidth + widthCosmeticAdd) + 'px';
//Make sure scrollbars are hidden
divHeaderColumn.style.overflow = "hidden";
//Set the zIndex to make sure this layer is above the orginal table
divHeaderColumn.style.zIndex = "99";
divHeaderColumn.style.position = "absolute";
divHeaderColumn.style.left = "0px";
//Move the column down - so that it starts below the header row
divHeaderColumn.style.top = (headerRow.offsetHeight + heightCosmeticAdd) + "px";
//Make sure the that when the user scoll down through the content - then the fixed column also scolls
addScrollSynchronization(divHeaderColumn, divContent, "vertical");
x = x - fixedColumnWidth;
}
if (horizontal)
{
if (vertical)
{
divContent.parentNode.insertBefore(divHeaderRowColumn, divContent);
}
divHeaderRowColumn.style.position = "absolute";
divHeaderRowColumn.style.left = "0px";
divHeaderRowColumn.style.top = "0px";
divHeaderRowColumn.style.width = (fixedColumnWidth + widthCosmeticAdd) + 'px';
divHeaderRowColumn.overflow = "hidden";
divHeaderRowColumn.style.zIndex = "100";
divHeaderRowColumn.style.backgroundColor = "#ffffff";
}
if (horizontal)
{
addScrollSynchronization(divHeaderRow, divContent, "horizontal");
}
if (horizontal || vertical)
{
window.onresize = ResizeScrollArea;
ResizeScrollArea();
}
}
}
function findFirstElementByType(startNode, search) {
if (! startNode.hasChildNodes()) return null;
var children = startNode.childNodes;
var i;
for (i = 0; i < children.length; i ++) {
if (children[i].nodeName.toUpperCase() == search.toUpperCase()) {
return children[i];
}
}
}
function findPosY(obj)
{
var curtop = 0;
if(obj.offsetParent)
while(1)
{
curtop += obj.offsetTop;
if(!obj.offsetParent)
break;
obj = obj.offsetParent;
}
else if(obj.y)
curtop += obj.y;
return curtop;
}
// Resize scroll area to window size.
function ResizeScrollArea()
{
var height = getInnerHeight() - findPosY(divHeaderRow) - 75;
if (!vertical)
{
height -= divHeaderRow.offsetHeight;
}
var width = getInnerWidth() - 50;
if (!horizontal)
{
width -= divHeaderColumn.offsetWidth;
}
var headerRowsWidth = 0;
if (divHeaderRowColumn != null)
{
headerRowsWidth = divHeaderRowColumn.offsetWidth;
}
// width
if (findFirstElementByType(divContent, 'table').offsetWidth > width)
{
divContent.style.width = Math.max(width - headerRowsWidth, 0) + 'px';
divContent.style.overflowX = "scroll";
divContent.style.overflowY = "auto";
}
else
{
divContent.style.width = x + 'px';
divContent.style.overflowX = "auto";
divContent.style.overflowY = "auto";
}
if (divHeaderRow != null)
{
divHeaderRow.style.width = (divContent.offsetWidth + headerRowsWidth) + 'px';
}
// height
if (findFirstElementByType(divContent, 'table').offsetHeight > height)
{
divContent.style.height = Math.max(height, 80) + 'px';
divContent.style.overflowY = "scroll";
}
else
{
divContent.style.height = y + 'px';
divContent.style.overflowY = "hidden";
}
if (divHeaderColumn != null)
{
divHeaderColumn.style.height = divContent.offsetHeight + 'px';
}
// check scrollbars
if (divContent.style.overflowY == "scroll")
{
divContent.style.width = (divContent.offsetWidth + 17) + 'px';
}
if (divContent.style.overflowX == "scroll")
{
divContent.style.height = (divContent.offsetHeight + 17) + 'px';
}
divContent.parentNode.style.width = (divContent.offsetWidth + headerRowsWidth) + 'px';
}
// next two functions from quirksmode.org
function getInnerHeight() {
var y;
if (self.innerHeight) // all except Explorer
{
y = self.innerHeight;
}
else if (document.documentElement && document.documentElement.clientHeight)
// Explorer 6 Strict Mode
{
y = document.documentElement.clientHeight;
}
else if (document.body) // other Explorers
{
y = document.body.clientHeight;
}
return y;
}
function getInnerWidth() {
var x;
if (self.innerWidth) // all except Explorer
{
x = self.innerWidth;
}
else if (document.documentElement && document.documentElement.clientWidth)
// Explorer 6 Strict Mode
{
x = document.documentElement.clientWidth;
}
else if (document.body) // other Explorers
{
x = document.body.clientWidth;
}
return x;
}
// ********************************************************************************
// Synchronize div elements when scrolling
// from http://webfx.eae.net/dhtml/syncscroll/syncscroll.html
// ********************************************************************************
// This is a function that returns a function that is used
// in the event listener
function getOnScrollFunction(oElement, srcElement) {
return function () {
if (oElement._scrollSyncDirection == "horizontal" || oElement._scrollSyncDirection == "both")
oElement.scrollLeft = srcElement.scrollLeft;
if (oElement._scrollSyncDirection == "vertical" || oElement._scrollSyncDirection == "both")
oElement.scrollTop = srcElement.scrollTop;
};
}
// This function adds scroll syncronization for the fromElement to the toElement
// this means that the fromElement will be updated when the toElement is scrolled
function addScrollSynchronization(fromElement, toElement, direction) {
removeScrollSynchronization(fromElement);
fromElement._syncScroll = getOnScrollFunction(fromElement, toElement);
fromElement._scrollSyncDirection = direction;
fromElement._syncTo = toElement;
if (toElement.addEventListener) {
toElement.addEventListener("scroll", fromElement._syncScroll, false);
} else {
toElement.attachEvent("onscroll", fromElement._syncScroll);
}
}
// removes the scroll synchronization for an element
function removeScrollSynchronization(fromElement) {
if (fromElement._syncTo != null) {
if (fromElement._syncTo.removeEventListener) {
fromElement._syncTo.removeEventListener("scroll", fromElement._syncScroll, false);
} else {
fromElement._syncTo.detachEvent("onscroll", fromElement._syncScroll);
}
}
fromElement._syncTo = null;
fromElement._syncScroll = null;
fromElement._scrollSyncDirection = null;
}
// browser detection routines from quirksmode.org
var BrowserDetect = {
init: function () {
this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
this.version = this.searchVersion(navigator.userAgent)
|| this.searchVersion(navigator.appVersion)
|| "an unknown version";
this.OS = this.searchString(this.dataOS) || "an unknown OS";
},
searchString: function (data) {
for (var i=0;i<data.length;i++) {
var dataString = data[i].string;
var dataProp = data[i].prop;
this.versionSearchString = data[i].versionSearch || data[i].identity;
if (dataString) {
if (dataString.indexOf(data[i].subString) != -1)
return data[i].identity;
}
else if (dataProp)
return data[i].identity;
}
},
searchVersion: function (dataString) {
var index = dataString.indexOf(this.versionSearchString);
if (index == -1) return;
return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
},
dataBrowser: [
{ string: navigator.userAgent,
subString: "OmniWeb",
versionSearch: "OmniWeb/",
identity: "OmniWeb"
},
{
string: navigator.vendor,
subString: "Apple",
identity: "Safari"
},
{
prop: window.opera,
identity: "Opera"
},
{
string: navigator.vendor,
subString: "iCab",
identity: "iCab"
},
{
string: navigator.vendor,
subString: "KDE",
identity: "Konqueror"
},
{
string: navigator.userAgent,
subString: "Firefox",
identity: "Firefox"
},
{
string: navigator.vendor,
subString: "Camino",
identity: "Camino"
},
{ // for newer Netscapes (6+)
string: navigator.userAgent,
subString: "Netscape",
identity: "Netscape"
},
{
string: navigator.userAgent,
subString: "MSIE",
identity: "Explorer",
versionSearch: "MSIE"
},
{
string: navigator.userAgent,
subString: "Gecko",
identity: "Mozilla",
versionSearch: "rv"
},
{ // for older Netscapes (4-)
string: navigator.userAgent,
subString: "Mozilla",
identity: "Netscape",
versionSearch: "Mozilla"
}
],
dataOS : [
{
string: navigator.platform,
subString: "Win",
identity: "Windows"
},
{
string: navigator.platform,
subString: "Mac",
identity: "Mac"
},
{
string: navigator.platform,
subString: "Linux",
identity: "Linux"
}
]
};
BrowserDetect.init();
</pre>
Hope this is useful - it solved my problem
|
|
|
|
|
Very Good Job!
Thx
|
|
|
|
|
It works very well!
Thank you very much! To you and Kenneth too.
|
|
|
|
|
I tried adding Kenneth's fix and it only partially works. I can scroll right to left, but not up and down. I wrapped the top row in a "thead" tag, and the rest of the rows in a "tbody" tag.
|
|
|
|
|
Scratch that, it works just fine. I just need to shrink the screen down to get the vertical scrollbar. Fantastic job, this is just what I was looking for.
|
|
|
|
|
It is not working in case of Mozzila firefox, Can anyone help,
|
|
|
|
|
Ok, I tried on IE and it's not working.
I mean. I want the table to have a fixed size (something like 500px) and the scroll to work.
But the table always show up big, the width and height on the css doesn't work when the javascript command 'CreateScrollHeader' is on (true).
What's wrong? I'm using IE7.
Tks
|
|
|
|
|
Thanks Karin for the above solution, it works great. However, i am facing a problem that events are not firing for the controls placed in fixed column headers. The page postback is happening but the event is not firing. The events for controls in non-fixed columns are firing. What could be the problem? Any help is appreciable...
modified on Tuesday, September 30, 2008 7:22 PM
|
|
|
|
|
I did tried lots of script and JQuery library, but if it's a big table with lots of rows few of this scripts make it slow, few script does not works in some browser. At last I found the best way to create the scrollable table with fixed row or column. I am using it in my huge ERP application, it is really fast and works great on Mozilla Firefox, IE, Opera and Google chrome. To see the detail please visit to
http://ektaraval.blogspot.com/2011/05/creating-scrollable-table-with-fixed.html
|
|
|
|
|
NOT WORKING - the url u referred not helps, can u reply
|
|
|
|
|
Thanks Karin for a great solution. In my scollable table I have checkboxes and when you click on them I have a function that checks the status of the checkbox, i.e. checked or not. The problem that I am experiencing is that, even when a checkbox is checked, it returns false. All my checkboxes have a unique id. I'm not sure how to solve this, I will appreciate any ideas.
Thanks, Asha.
|
|
|
|
|
Because the javascipt duplicates the contents of the table to the top and left, multiple instances of your form elements will be created. This might result in different results when collecting the form elements when submitted.
Gilmore
|
|
|
|
|
can anyone please suggest a solution for this issue.
|
|
|
|
|
Great piece of work here thanks! Does anyone have any ideas on altering this code so that you could freeze multiple left hand side columns? Haven't figured out how to do that using this solution yet.
Thanks!
|
|
|
|
|
First off I must say "WOW". This is some great code. But have you figured out yet how to modify this to freeze more than 1 column? It would be awesome to have the first 3-4 left columns to freeze as well. I've just spent about 3 weeks banging my head against the CSS wall trying to this and I'm just amazed at the speed. Thanks.
|
|
|
|
|
Here's the solution I used-
Nest a table in each of the first (fixed) cells with the columns you want.
You will need to use fixed width for these columns as the tables don't have any relation.
You'll also want to override the top / bottom border styles on these new columns so you don't get a double border.
|
|
|
|
|
This is what I want to do but I'm not sure how to implement what you suggest. I have 3 'title cells' to freeze on each row (the ones with class="reportRowHeader"). Here is the HTML section that is working now to freeze 1 cell on each row. Can you give me an example in this section to freeze the 3 columns? If you could give an example of how to do that with a dynamic number of columns (ie. all reportRowHeader columns) that completely solve my problem. Thanks.
<div id = "outerDiv">
<div id = "innerDiv">
<table class = "reportTable" cellspacing = "0">
<tr>
<td class = "reportCorner"> </td>
<td class = "reportCorner"> </td>
<td class = "reportCorner"> </td>
<td class = "reportColumnHeader">Albania</td>
<td class = "reportColumnHeader">Argentina</td>
<td class = "reportColumnHeader">Australia</td>
<td class = "reportColumnHeader">Denmark</td>
<td class = "reportColumnHeader">Finland</td>
<td class = "reportColumnHeader">Iceland</td>
<td class = "reportColumnHeader">Norway</td>
<td class = "reportColumnHeader">Sweden</td>
<td class = "reportColumnHeader">Number</td>
<td class = "reportColumnHeader">Percentage (of the whole sample)</td>
</tr>
<tr>
<td class = "reportRowHeader">Q.4 Legal Framework - What is the legal basis for the following?</td>
<td class = "reportRowHeader"> </td>
<td class = "reportRowHeader"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
</tr>
<tr>
<td class = "reportRowHeader"> </td>
<td class = "reportRowHeader">4.a The form and structure of the annual budget and related legislation</td>
<td class = "reportRowHeader"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
</tr>
<tr>
<td class = "reportRowHeader"> </td>
<td class = "reportRowHeader"> </td>
<td class = "reportRowHeader">Constitution</td>
<td class = "reportDataCell">1</td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell">1</td>
<td class = "reportDataCell">1</td>
<td class = "reportDataCell">1</td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell"> </td>
<td class = "reportDataCell">4</td>
<td class = "reportDataCell">50.0%</td>
</tr>
</table>
</div>
</div>
|
|
|
|
|
This solution is excellent! I have been trying to do this for some time. I was wondering about wrapping the text in the table cells, however. Before implementing your solution, my table looked very nice but was far less usable due to the non-fixed headers. Now my table works great but it contains some incredibly wide cells because the text is not wrapping.
I admit I am not a javascript expert by any means. Is there a relatively easy way to fix this and still maintain the fixed headers?
Thanks so much.
Andy
|
|
|
|
|
Hello all,
After some work, I have managed to get this script working in all of the major browsers, including IE, Firefox, Opera (buggy though), Konqueror, and Safari.
The only major difference from the original script is the table header MUST be enclosed in a <thead>, and the body must be in a <tbody> tag to work right.
var divContent = null;
var divHeaderRow = null;
var divHeaderColumn = null;
var divHeaderRowColumn = null;
var headerRowFirstColumn = null;
var x;
var y;
var horizontal = false;
var vertical = false;
// Copy table to top and to left
function CreateScrollHeader(content, scrollHorizontal, scrollVertical)
{
horizontal = scrollHorizontal;
vertical = scrollVertical;
divContent = content;
if(BrowserDetect.browser == "Konqueror") {
window.onload = CreateScrollHeaderActual;
} else {
CreateScrollHeaderActual();
}
}
function CreateScrollHeaderActual() {
if (divContent != null)
{
var widthCosmeticAdd = 0;
var heightCosmeticAdd = 0;
// browser specific cosmetic tweaks
if (BrowserDetect.browser == "Explorer") {
widthCosmeticAdd = 2;
heightCosmeticAdd = 2;
} else if(BrowserDetect.browser == "Opera" || BrowserDetect.browser == "Konqueror" || BrowserDetect.browser == "Safari") {
widthCosmeticAdd = 1;
heightCosmeticAdd = 1;
}
var originalTable = findFirstElementByType(divContent, 'table');
var headerRow = findFirstElementByType(originalTable, 'thead');
x = originalTable.offsetWidth;
y = originalTable.offsetHeight;
divHeaderRow = divContent.cloneNode(true);
if (horizontal)
{
divHeaderRow.style.height = (headerRow.offsetHeight + heightCosmeticAdd) + 'px';
divHeaderRow.style.overflow = "hidden";
divContent.parentNode.insertBefore(divHeaderRow, divContent);
originalTable.style.position = "absolute";
originalTable.style.top = "-" + (headerRow.offsetHeight + heightCosmeticAdd) + 'px';
y = y - headerRow.offsetHeight;
}
divHeaderRowColumn = divHeaderRow.cloneNode(true);
headerRowFirstColumn = findFirstElementByType(findFirstElementByType(headerRow,'tr'), 'th');
divHeaderColumn = divContent.cloneNode(true);
divContent.style.position = "relative";
if (vertical)
{
divContent.parentNode.insertBefore(divHeaderColumn, divContent);
divContent.style.left = headerRowFirstColumn.offsetWidth + 'px';
originalTable.style.position = "absolute";
originalTable.style.left = "-" + headerRowFirstColumn.offsetWidth + 'px';
}
else
{
divContent.style.left = "0px";
}
if (vertical)
{
divHeaderColumn.style.width = (headerRowFirstColumn.offsetWidth + widthCosmeticAdd) + 'px';
divHeaderColumn.style.overflow = "hidden";
divHeaderColumn.style.zIndex = "99";
divHeaderColumn.style.position = "absolute";
divHeaderColumn.style.left = "0px";
divHeaderColumn.style.top = (headerRow.offsetHeight + heightCosmeticAdd) + "px";
addScrollSynchronization(divHeaderColumn, divContent, "vertical");
x = x - headerRowFirstColumn.offsetWidth;
}
if (horizontal)
{
if (vertical)
{
divContent.parentNode.insertBefore(divHeaderRowColumn, divContent);
}
divHeaderRowColumn.style.position = "absolute";
divHeaderRowColumn.style.left = "0px";
divHeaderRowColumn.style.top = "0px";
divHeaderRowColumn.style.width = (headerRowFirstColumn.offsetWidth + widthCosmeticAdd) + 'px';
divHeaderRowColumn.overflow = "hidden";
divHeaderRowColumn.style.zIndex = "100";
divHeaderRowColumn.style.backgroundColor = "#ffffff";
}
if (horizontal)
{
addScrollSynchronization(divHeaderRow, divContent, "horizontal");
}
if (horizontal || vertical)
{
window.onresize = ResizeScrollArea;
ResizeScrollArea();
}
}
}
function findFirstElementByType(startNode, search) {
if (! startNode.hasChildNodes()) return null;
var children = startNode.childNodes;
var i;
for (i = 0; i < children.length; i ++) {
if (children[i].nodeName.toUpperCase() == search.toUpperCase()) {
return children[i];
}
}
}
// Resize scroll area to window size.
function ResizeScrollArea()
{
var height = getInnerHeight() - findPosY(divHeaderRow) - 75;
if (!vertical)
{
height -= divHeaderRow.offsetHeight;
}
var width = getInnerWidth() - 50;
if (!horizontal)
{
width -= divHeaderColumn.offsetWidth;
}
var headerRowsWidth = 0;
// divContent.childNodes[0].style.width = x + 'px';
// divContent.childNodes[0].style.height = y + 'px';
if (divHeaderRowColumn != null)
{
headerRowsWidth = divHeaderRowColumn.offsetWidth;
}
// width
if (findFirstElementByType(divContent, 'table').offsetWidth > width)
{
divContent.style.width = Math.max(width - headerRowsWidth, 0) + 'px';
divContent.style.overflowX = "scroll";
divContent.style.overflowY = "auto";
}
else
{
divContent.style.width = x + 'px';
divContent.style.overflowX = "auto";
divContent.style.overflowY = "auto";
}
if (divHeaderRow != null)
{
divHeaderRow.style.width = (divContent.offsetWidth + headerRowsWidth) + 'px';
}
// height
if (findFirstElementByType(divContent, 'table').offsetHeight > height)
{
divContent.style.height = Math.max(height, 80) + 'px';
divContent.style.overflowY = "scroll";
}
else
{
divContent.style.height = y + 'px';
divContent.style.overflowY = "hidden";
}
if (divHeaderColumn != null)
{
divHeaderColumn.style.height = divContent.offsetHeight + 'px';
}
// check scrollbars
if (divContent.style.overflowY == "scroll")
{
divContent.style.width = (divContent.offsetWidth + 17) + 'px';
}
if (divContent.style.overflowX == "scroll")
{
divContent.style.height = (divContent.offsetHeight + 17) + 'px';
}
divContent.parentNode.style.width = (divContent.offsetWidth + headerRowsWidth) + 'px';
}
// next two functions from quirksmode.org
function getInnerHeight() {
var y;
if (self.innerHeight) // all except Explorer
{
y = self.innerHeight;
}
else if (document.documentElement && document.documentElement.clientHeight)
// Explorer 6 Strict Mode
{
y = document.documentElement.clientHeight;
}
else if (document.body) // other Explorers
{
y = document.body.clientHeight;
}
return y;
}
function getInnerWidth() {
var x;
if (self.innerWidth) // all except Explorer
{
x = self.innerWidth;
}
else if (document.documentElement && document.documentElement.clientWidth)
// Explorer 6 Strict Mode
{
x = document.documentElement.clientWidth;
}
else if (document.body) // other Explorers
{
x = document.body.clientWidth;
}
return x;
}
// ********************************************************************************
// Synchronize div elements when scrolling
// from http://webfx.eae.net/dhtml/syncscroll/syncscroll.html
// ********************************************************************************
// This is a function that returns a function that is used
// in the event listener
function getOnScrollFunction(oElement, srcElement) {
return function () {
if (oElement._scrollSyncDirection == "horizontal" || oElement._scrollSyncDirection == "both")
oElement.scrollLeft = srcElement.scrollLeft;
if (oElement._scrollSyncDirection == "vertical" || oElement._scrollSyncDirection == "both")
oElement.scrollTop = srcElement.scrollTop;
};
}
// This function adds scroll syncronization for the fromElement to the toElement
// this means that the fromElement will be updated when the toElement is scrolled
function addScrollSynchronization(fromElement, toElement, direction) {
removeScrollSynchronization(fromElement);
fromElement._syncScroll = getOnScrollFunction(fromElement, toElement);
fromElement._scrollSyncDirection = direction;
fromElement._syncTo = toElement;
if (toElement.addEventListener) {
toElement.addEventListener("scroll", fromElement._syncScroll, false);
} else {
toElement.attachEvent("onscroll", fromElement._syncScroll);
}
}
// removes the scroll synchronization for an element
function removeScrollSynchronization(fromElement) {
if (fromElement._syncTo != null) {
if (fromElement._syncTo.removeEventListener) {
fromElement._syncTo.removeEventListener("scroll", fromElement._syncScroll, false);
} else {
fromElement._syncTo.detachEvent("onscroll", fromElement._syncScroll);
}
}
fromElement._syncTo = null;
fromElement._syncScroll = null;
fromElement._scrollSyncDirection = null;
}
// browser detection routines from quirksmode.org
var BrowserDetect = {
init: function () {
this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
this.version = this.searchVersion(navigator.userAgent)
|| this.searchVersion(navigator.appVersion)
|| "an unknown version";
this.OS = this.searchString(this.dataOS) || "an unknown OS";
},
searchString: function (data) {
for (var i=0;i<data.length;i++) {
var dataString = data[i].string;
var dataProp = data[i].prop;
this.versionSearchString = data[i].versionSearch || data[i].identity;
if (dataString) {
if (dataString.indexOf(data[i].subString) != -1)
return data[i].identity;
}
else if (dataProp)
return data[i].identity;
}
},
searchVersion: function (dataString) {
var index = dataString.indexOf(this.versionSearchString);
if (index == -1) return;
return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
},
dataBrowser: [
{ string: navigator.userAgent,
subString: "OmniWeb",
versionSearch: "OmniWeb/",
identity: "OmniWeb"
},
{
string: navigator.vendor,
subString: "Apple",
identity: "Safari"
},
{
prop: window.opera,
identity: "Opera"
},
{
string: navigator.vendor,
subString: "iCab",
identity: "iCab"
},
{
string: navigator.vendor,
subString: "KDE",
identity: "Konqueror"
},
{
string: navigator.userAgent,
subString: "Firefox",
identity: "Firefox"
},
{
string: navigator.vendor,
subString: "Camino",
identity: "Camino"
},
{ // for newer Netscapes (6+)
string: navigator.userAgent,
subString: "Netscape",
identity: "Netscape"
},
{
string: navigator.userAgent,
subString: "MSIE",
identity: "Explorer",
versionSearch: "MSIE"
},
{
string: navigator.userAgent,
subString: "Gecko",
identity: "Mozilla",
versionSearch: "rv"
},
{ // for older Netscapes (4-)
string: navigator.userAgent,
subString: "Mozilla",
identity: "Netscape",
versionSearch: "Mozilla"
}
],
dataOS : [
{
string: navigator.platform,
subString: "Win",
identity: "Windows"
},
{
string: navigator.platform,
subString: "Mac",
identity: "Mac"
},
{
string: navigator.platform,
subString: "Linux",
identity: "Linux"
}
]
};
BrowserDetect.init();
I hope this helps somebody, and thank you Karin for a great idea.
kdb
-- modified at 22:53 Tuesday 10th October, 2006
|
|
|
|
|
The function findPosY is missing.
|
|
|
|
|
Hi,
Thanks for this good script
I found that from http://www.experts-exchange.com/Programming/Languages/Scripting/JavaScript/Q_21761515.html:
function findPosY(obj) {<br />
var curTop= obj.offsetTop;<br />
while((obj= obj.offsetParent) != null) {<br />
curTop+= obj.offsetTop;<br />
}
return curTop;<br />
}
Put it in the script and all work (I tested on Firefox 3.5.16 and IE8)
I hope this will help you
Herve
|
|
|
|
|