A Better Fixed GridView Header for ASP.NET
In the listing, the function Foo is treated like a class and the statement alert... basically plays the role of initialization (or construct) code. The statement var foo = new Foo(); ultimately creates an instance of Foo (upper case) and runs the initialization code.
You could move the alert statement to a function within Foo and invoke that function. This will give you a better sense of the object-oriented nature of JavaScript when used in this manner (see Listing 4).
Listing 4: Adding a member function to Foo.
<%@ Page Language="VB" AutoEventWireup="false"
CodeFile="Default2.aspx.vb" Inherits="Default2" %>
<!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 runat="server">
<title>Untitled Page</title>
<script language="javascript">
function Foo()
{
this.MemberFunc = memberFunc;
function memberFunc()
{
alert("This is a simple JavaScript class");
}
}
var foo = new Foo();
foo.MemberFunc();
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
</div>
</form>
</body>
</html>
In the revision, Foo has a member function called memberFunc (notice the case) and makes it accessible externally by assigning it to this.MemberFunc. Without the this.member statement, the member is treated as inaccessible externally.
Cloning the Grid Header
To begin wrapping things up, you now can implement what I refer to as the PositionClass. The PositionClass will figure out where the grid is, where the header is, and keep track of where the header should be depending on the scroll position and the size of the window containing the grid. Listing 5 is a couple of hundred lines long, but a complete explanation is offered after the listing. To get started, add a new JavaScript file to your solution and put the code in Listing 5 in that file.
Listing 5: JavaScript code to manage and position a row containing header information.
// JScript File - by PTK
// Implements a fixed header for HTML tables (like a GridView,
// which renders and HTML table)
function PositionClass()
{
this.Top = top;
this.Left = left;
this.Width = width;
this.Height = height;
this.ClientID = clientID;
this.SavePosition = savePosition;
this.Reposition = reposition;
this.SetClientID = setClientID;
var top, left, width, height, clientID;
var head, caption;
// the client ID of the table/gridview
function setClientID(id)
{
clientID = id;
}
// determines the absolute position of X or Y--determined by
// the getOffset function to handle control nesting
function getAbsolutePosition(control, getOffset, adjustCaption)
{
var result = 0;
while(control)
{
if(control.tagName)
if((control.tagName == "TBODY") ||
(control.tagName == "TR"))
{
if(control.parentElement)
control = control.parentElement;
else
break;
continue;
}
if(control.style.position == "absolute")
return result;
result += getOffset(control);
if(control.parentElement)
control = control.parentElement;
else
break;
}
if(adjustCaption && caption && isAlignedTop(caption))
result -= caption.clientHeight;
return result;
}
// returns the x offset
function getXOffset(control)
{
if(control.offsetLeft)
return control.offsetLeft;
else
return 0;
}
// returns the y offset
function getYOffset(control)
{
if(control.offsetTop)
return control.offsetTop;
else
return 0;
}
// get the grid header row. It may not exist or there may be a
// caption above it
function getHeaderNode(grid)
{
if(!grid) return null;
for(var i=0; i<grid.rows.length; i++)
{
var s = new String();
if(grid.rows[i].childNodes.length > 0)
{
s = grid.rows[i].childNodes[0].tagName;
if(s.toLowerCase() == "th")
return grid.rows[i];
}
}
return null;
}
// everything but "bottom" is top=aligned
function isAlignedTop(theCaption)
{
if(!theCaption) return false;
var tag = theCaption.align.toLowerCase();
return (tag == "top") || (tag == "left") || (tag == "right")
|| (tag == "");
}
// find the caption; we have to allow for a caption above the
// header
function getCaptionNode(grid)
{
if(!grid) return null;
return grid.caption;
}
// stores the current position of the control
function savePosition()
{
// debugger;
// get the grid
var grid = document.all[clientID];
if(!grid) return;
header.cellPadding = grid.cellPadding;
header.cellSpacing = grid.cellSpacing;
header.border = grid.border;
header.bgColor = grid.bgColor;
header.className = grid.className;
caption = getCaptionNode(grid);
if(caption && (caption.style.backgroundColor == ""))
{
// debugger;
caption.style.backgroundColor = "white";
}
head = getHeaderNode(grid);
if(head == null) return;
head.style.visibility = "hidden";
// get the header position
top = getAbsolutePosition(head, getYOffset, true);
left = getAbsolutePosition(head, getXOffset, false);
width = head.clientWidth;
height = head.clientHeight;
var temp = head.cloneNode(true);
// store sizes in style attributes
for(var i=0; i<head.childNodes.length; i++)
{
temp.childNodes[i].style.width =
head.childNodes[i].clientWidth;
temp.childNodes[i].style.height =
head.childNodes[i].clientHeight;
}
head = temp;
// clone the header
head.style.visibility = "visible";
// insert the header (and caption) into our copy table
if(caption && isAlignedTop(caption))
{
var newCaption = caption.cloneNode(true);
if(header.caption)
header.replaceChild(newCaption, header.caption);
else
header.appendChild(newCaption);
caption.style.visibility = "hidden";
}
// place the new table header in the table-playing header
var th = getHeaderNode(header);
if(th)
header.childNodes[0].replaceChild(head, th);
else
header.childNodes[0].appendChild(head);
// fix the position of the div to overlap the gridview's header
fixedHeader.style.posLeft = left;
fixedHeader.style.left = left + "px";
fixedHeader.style.posTop = top;
fixedHeader.style.top = top + "px";
fixedHeader.style.width = width + "px";
fixedHeader.style.height = height + "px";
fixedHeader.style.visibility = "visible";
fixedHeader.style.zIndex = 0;
}
// repositions the control if necessary
function reposition()
{
if(!document.all[clientID]) return;
// added silent try..catch because masterpages can have other
// things that scroll;
// for instance we scroll the treeview that fires this event
try
{
if(document.body.parentNode.scrollLeft > 0)
{
fixedHeader.style.posLeft =
document.body.parentNode.scrollLeft;
fixedHeader.style.left =
document.body.parentNode.scrollLeft + "px";
}
else
{
fixedHeader.style.posLeft =
document.body.parentNode.scrollLeft + left;
fixedHeader.style.left =
document.body.parentNode.scrollLeft + left + "px";
}
if(document.body.parentNode.scrollTop > 0)
{
fixedHeader.style.top =
document.body.parentNode.scrollTop + "px";
fixedHeader.style.posTop =
document.body.parentNode.scrollTop;
}
else
{
fixedHeader.style.top =
document.body.parentNode.scrollTop + top + "px";
fixedHeader.style.posTop =
document.body.parentNode.scrollTop + top;
}
fixedHeader.style.width = width + "px";
fixedHeader.style.height = height + "px";
fixedHeader.style.visibility = "visible";
}
catch(oException)
{
}
}
}



Solid state disks (SSDs) made a splash in consumer technology, and now the technology has its eyes on the enterprise storage market. Download this eBook to see what SSDs can do for your infrastructure and review the pros and cons of this potentially game-changing storage technology.