4 * Description: "Fix" a header at the top of the table, so it scrolls with the table
5 * Author: Allan Jardine (www.sprymedia.co.uk)
6 * Created: Wed 16 Sep 2009 19:46:30 BST
8 * License: GPL v2 or BSD 3 point style
9 * Project: Just a little bit of fun - enjoy :-)
10 * Contact: www.sprymedia.co.uk/contact
12 * Copyright 2009-2012 Allan Jardine, all rights reserved.
14 * This source file is free software, under either the GPL v2 license or a
15 * BSD style license, available at:
16 * http://datatables.net/license_gpl2
17 * http://datatables.net/license_bsd
21 * Function: FixedHeader
22 * Purpose: Provide 'fixed' header, footer and columns on an HTML table
23 * Returns: object:FixedHeader - must be called with 'new'
24 * Inputs: mixed:mTable - target table
25 * 1. DataTable object - when using FixedHeader with DataTables, or
26 * 2. HTML table node - when using FixedHeader without DataTables
27 * object:oInit - initialisation settings, with the following properties (each optional)
28 * bool:top - fix the header (default true)
29 * bool:bottom - fix the footer (default false)
30 * bool:left - fix the left most column (default false)
31 * bool:right - fix the right most column (default false)
32 * int:zTop - fixed header zIndex
33 * int:zBottom - fixed footer zIndex
34 * int:zLeft - fixed left zIndex
35 * int:zRight - fixed right zIndex
37 var FixedHeader = function ( mTable, oInit ) {
38 /* Sanity check - you just know it will happen */
39 if ( typeof this.fnInit != 'function' )
41 alert( "FixedHeader warning: FixedHeader must be initialised with the 'new' keyword." );
64 "iTableRight": 0, /* note this is left+width, not actually "right" */
66 "iTableBottom": 0 /* note this is top+height, not actually "bottom" */
77 * Function: fnGetSettings
78 * Purpose: Get the settings for this object
79 * Returns: object: - settings object
82 this.fnGetSettings = function () {
88 * Purpose: Update the positioning and copies of the fixed elements
92 this.fnUpdate = function () {
93 this._fnUpdateClones();
94 this._fnUpdatePositions();
98 * Function: fnPosition
99 * Purpose: Update the positioning of the fixed elements
103 this.fnPosition = function () {
104 this._fnUpdatePositions();
108 this.fnInit( mTable, oInit );
110 /* Store the instance on the DataTables object for easy access */
111 if ( typeof mTable.fnSettings == 'function' )
113 mTable._oPluginFixedHeader = this;
119 * Variable: FixedHeader
120 * Purpose: Prototype for FixedHeader
123 FixedHeader.prototype = {
124 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
130 * Purpose: The "constructor"
132 * Inputs: {as FixedHeader function}
134 fnInit: function ( oTable, oInit )
136 var s = this.fnGetSettings();
139 /* Record the user definable settings */
140 this.fnInitSettings( s, oInit );
142 /* DataTables specific stuff */
143 if ( typeof oTable.fnSettings == 'function' )
145 if ( typeof oTable.fnVersionCheck == 'functon' &&
146 oTable.fnVersionCheck( '1.6.0' ) !== true )
148 alert( "FixedHeader 2 required DataTables 1.6.0 or later. "+
149 "Please upgrade your DataTables installation" );
153 var oDtSettings = oTable.fnSettings();
155 if ( oDtSettings.oScroll.sX != "" || oDtSettings.oScroll.sY != "" )
157 alert( "FixedHeader 2 is not supported with DataTables' scrolling mode at this time" );
161 s.nTable = oDtSettings.nTable;
162 oDtSettings.aoDrawCallback.push( {
164 FixedHeader.fnMeasure();
165 that._fnUpdateClones.call(that);
166 that._fnUpdatePositions.call(that);
168 "sName": "FixedHeader"
176 s.bFooter = ($('>tfoot', s.nTable).length > 0) ? true : false;
178 /* "Detect" browsers that don't support absolute positioing - or have bugs */
179 s.bUseAbsPos = (jQuery.browser.msie && (jQuery.browser.version=="6.0"||jQuery.browser.version=="7.0"));
181 /* Add the 'sides' that are fixed */
184 s.aoCache.push( that._fnCloneTable( "fixedHeader", "FixedHeader_Header", that._fnCloneThead ) );
186 if ( s.oSides.bottom )
188 s.aoCache.push( that._fnCloneTable( "fixedFooter", "FixedHeader_Footer", that._fnCloneTfoot ) );
192 s.aoCache.push( that._fnCloneTable( "fixedLeft", "FixedHeader_Left", that._fnCloneTLeft ) );
194 if ( s.oSides.right )
196 s.aoCache.push( that._fnCloneTable( "fixedRight", "FixedHeader_Right", that._fnCloneTRight ) );
199 /* Event listeners for window movement */
200 FixedHeader.afnScroll.push( function () {
201 that._fnUpdatePositions.call(that);
204 jQuery(window).resize( function () {
205 FixedHeader.fnMeasure();
206 that._fnUpdateClones.call(that);
207 that._fnUpdatePositions.call(that);
210 /* Get things right to start with */
211 FixedHeader.fnMeasure();
212 that._fnUpdateClones();
213 that._fnUpdatePositions();
217 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
222 * Function: fnInitSettings
223 * Purpose: Take the user's settings and copy them to our local store
225 * Inputs: object:s - the local settings object
226 * object:oInit - the user's settings object
228 fnInitSettings: function ( s, oInit )
230 if ( typeof oInit != 'undefined' )
232 if ( typeof oInit.top != 'undefined' ) {
233 s.oSides.top = oInit.top;
235 if ( typeof oInit.bottom != 'undefined' ) {
236 s.oSides.bottom = oInit.bottom;
238 if ( typeof oInit.left != 'undefined' ) {
239 s.oSides.left = oInit.left;
241 if ( typeof oInit.right != 'undefined' ) {
242 s.oSides.right = oInit.right;
245 if ( typeof oInit.zTop != 'undefined' ) {
246 s.oZIndexes.top = oInit.zTop;
248 if ( typeof oInit.zBottom != 'undefined' ) {
249 s.oZIndexes.bottom = oInit.zBottom;
251 if ( typeof oInit.zLeft != 'undefined' ) {
252 s.oZIndexes.left = oInit.zLeft;
254 if ( typeof oInit.zRight != 'undefined' ) {
255 s.oZIndexes.right = oInit.zRight;
258 if ( typeof oInit.offsetTop != 'undefined' ) {
259 s.oOffset.top = oInit.offsetTop;
263 /* Detect browsers which have poor position:fixed support so we can use absolute positions.
264 * This is much slower since the position must be updated for each scroll, but widens
267 s.bUseAbsPos = (jQuery.browser.msie &&
268 (jQuery.browser.version=="6.0"||jQuery.browser.version=="7.0"));
272 * Function: _fnCloneTable
273 * Purpose: Clone the table node and do basic initialisation
277 _fnCloneTable: function ( sType, sClass, fnClone )
279 var s = this.fnGetSettings();
282 /* We know that the table _MUST_ has a DIV wrapped around it, because this is simply how
283 * DataTables works. Therefore, we can set this to be relatively position (if it is not
284 * alreadu absolute, and use this as the base point for the cloned header
286 if ( jQuery(s.nTable.parentNode).css('position') != "absolute" )
288 s.nTable.parentNode.style.position = "relative";
291 /* Just a shallow clone will do - we only want the table node */
292 nCTable = s.nTable.cloneNode( false );
293 nCTable.removeAttribute( 'id' );
295 var nDiv = document.createElement( 'div' );
296 nDiv.style.position = "absolute";
297 nDiv.style.top = "0px";
298 nDiv.style.left = "0px";
299 nDiv.className += " FixedHeader_Cloned "+sType+" "+sClass;
301 /* Set the zIndexes */
302 if ( sType == "fixedHeader" )
304 nDiv.style.zIndex = s.oZIndexes.top;
306 if ( sType == "fixedFooter" )
308 nDiv.style.zIndex = s.oZIndexes.bottom;
310 if ( sType == "fixedLeft" )
312 nDiv.style.zIndex = s.oZIndexes.left;
314 else if ( sType == "fixedRight" )
316 nDiv.style.zIndex = s.oZIndexes.right;
319 /* remove margins since we are going to poistion it absolutely */
320 nCTable.style.margin = "0";
322 /* Insert the newly cloned table into the DOM, on top of the "real" header */
323 nDiv.appendChild( nCTable );
324 document.body.appendChild( nDiv );
338 * Function: _fnUpdatePositions
339 * Purpose: Get the current positioning of the table in the DOM
343 _fnMeasure: function ()
346 s = this.fnGetSettings(),
348 jqTable = jQuery(s.nTable),
349 oOffset = jqTable.offset(),
350 iParentScrollTop = this._fnSumScroll( s.nTable.parentNode, 'scrollTop' ),
351 iParentScrollLeft = this._fnSumScroll( s.nTable.parentNode, 'scrollLeft' );
353 m.iTableWidth = jqTable.outerWidth();
354 m.iTableHeight = jqTable.outerHeight();
355 m.iTableLeft = oOffset.left + s.nTable.parentNode.scrollLeft;
356 m.iTableTop = oOffset.top + iParentScrollTop;
357 m.iTableRight = m.iTableLeft + m.iTableWidth;
358 m.iTableRight = FixedHeader.oDoc.iWidth - m.iTableLeft - m.iTableWidth;
359 m.iTableBottom = FixedHeader.oDoc.iHeight - m.iTableTop - m.iTableHeight;
363 * Function: _fnSumScroll
364 * Purpose: Sum node parameters all the way to the top
366 * Inputs: node:n - node to consider
367 * string:side - scrollTop or scrollLeft
369 _fnSumScroll: function ( n, side )
372 while ( n = n.parentNode )
374 if ( n.nodeName == 'HTML' || n.nodeName == 'BODY' )
384 * Function: _fnUpdatePositions
385 * Purpose: Loop over the fixed elements for this table and update their positions
389 _fnUpdatePositions: function ()
391 var s = this.fnGetSettings();
394 for ( var i=0, iLen=s.aoCache.length ; i<iLen ; i++ )
396 if ( s.aoCache[i].sType == "fixedHeader" )
398 this._fnScrollFixedHeader( s.aoCache[i] );
400 else if ( s.aoCache[i].sType == "fixedFooter" )
402 this._fnScrollFixedFooter( s.aoCache[i] );
404 else if ( s.aoCache[i].sType == "fixedLeft" )
406 this._fnScrollHorizontalLeft( s.aoCache[i] );
410 this._fnScrollHorizontalRight( s.aoCache[i] );
416 * Function: _fnUpdateClones
417 * Purpose: Loop over the fixed elements for this table and call their cloning functions
421 _fnUpdateClones: function ()
423 var s = this.fnGetSettings();
424 for ( var i=0, iLen=s.aoCache.length ; i<iLen ; i++ )
426 s.aoCache[i].fnClone.call( this, s.aoCache[i] );
431 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
432 * Scrolling functions
436 * Function: _fnScrollHorizontalLeft
437 * Purpose: Update the positioning of the scrolling elements
439 * Inputs: object:oCache - the cahced values for this fixed element
441 _fnScrollHorizontalRight: function ( oCache )
444 s = this.fnGetSettings(),
446 oWin = FixedHeader.oWin,
447 oDoc = FixedHeader.oDoc,
448 nTable = oCache.nWrapper,
449 iFixedWidth = jQuery(nTable).outerWidth();
451 if ( oWin.iScrollRight < oMes.iTableRight )
453 /* Fully right aligned */
454 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
455 this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
456 this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft+oMes.iTableWidth-iFixedWidth)+"px", 'left', nTable.style );
458 else if ( oMes.iTableLeft < oDoc.iWidth-oWin.iScrollRight-iFixedWidth )
463 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
464 this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
465 this._fnUpdateCache( oCache, 'sLeft', (oDoc.iWidth-oWin.iScrollRight-iFixedWidth)+"px", 'left', nTable.style );
469 this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
470 this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop-oWin.iScrollTop)+"px", 'top', nTable.style );
471 this._fnUpdateCache( oCache, 'sLeft', (oWin.iWidth-iFixedWidth)+"px", 'left', nTable.style );
476 /* Fully left aligned */
477 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
478 this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
479 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
484 * Function: _fnScrollHorizontalLeft
485 * Purpose: Update the positioning of the scrolling elements
487 * Inputs: object:oCache - the cahced values for this fixed element
489 _fnScrollHorizontalLeft: function ( oCache )
492 s = this.fnGetSettings(),
494 oWin = FixedHeader.oWin,
495 oDoc = FixedHeader.oDoc,
496 nTable = oCache.nWrapper,
497 iCellWidth = jQuery(nTable).outerWidth();
499 if ( oWin.iScrollLeft < oMes.iTableLeft )
501 /* Fully left align */
502 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
503 this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
504 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
506 else if ( oWin.iScrollLeft < oMes.iTableLeft+oMes.iTableWidth-iCellWidth )
511 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
512 this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
513 this._fnUpdateCache( oCache, 'sLeft', oWin.iScrollLeft+"px", 'left', nTable.style );
517 this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
518 this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop-oWin.iScrollTop)+"px", 'top', nTable.style );
519 this._fnUpdateCache( oCache, 'sLeft', "0px", 'left', nTable.style );
524 /* Fully right align */
525 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
526 this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
527 this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft+oMes.iTableWidth-iCellWidth)+"px", 'left', nTable.style );
532 * Function: _fnScrollFixedFooter
533 * Purpose: Update the positioning of the scrolling elements
535 * Inputs: object:oCache - the cahced values for this fixed element
537 _fnScrollFixedFooter: function ( oCache )
540 s = this.fnGetSettings(),
542 oWin = FixedHeader.oWin,
543 oDoc = FixedHeader.oDoc,
544 nTable = oCache.nWrapper,
545 iTheadHeight = jQuery("thead", s.nTable).outerHeight(),
546 iCellHeight = jQuery(nTable).outerHeight();
548 if ( oWin.iScrollBottom < oMes.iTableBottom )
551 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
552 this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+oMes.iTableHeight-iCellHeight)+"px", 'top', nTable.style );
553 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
555 else if ( oWin.iScrollBottom < oMes.iTableBottom+oMes.iTableHeight-iCellHeight-iTheadHeight )
560 this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
561 this._fnUpdateCache( oCache, 'sTop', (oDoc.iHeight-oWin.iScrollBottom-iCellHeight)+"px", 'top', nTable.style );
562 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
566 this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
567 this._fnUpdateCache( oCache, 'sTop', (oWin.iHeight-iCellHeight)+"px", 'top', nTable.style );
568 this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft-oWin.iScrollLeft)+"px", 'left', nTable.style );
574 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
575 this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+iCellHeight)+"px", 'top', nTable.style );
576 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
581 * Function: _fnScrollFixedHeader
582 * Purpose: Update the positioning of the scrolling elements
584 * Inputs: object:oCache - the cahced values for this fixed element
586 _fnScrollFixedHeader: function ( oCache )
589 s = this.fnGetSettings(),
591 oWin = FixedHeader.oWin,
592 oDoc = FixedHeader.oDoc,
593 nTable = oCache.nWrapper,
595 anTbodies = s.nTable.getElementsByTagName('tbody');
597 for (var i = 0; i < anTbodies.length; ++i) {
598 iTbodyHeight += anTbodies[i].offsetHeight;
601 if ( oMes.iTableTop > oWin.iScrollTop + s.oOffset.top )
603 /* Above the table */
604 this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
605 this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
606 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
608 else if ( oWin.iScrollTop + s.oOffset.top > oMes.iTableTop+iTbodyHeight )
610 /* At the bottom of the table */
611 this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
612 this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+iTbodyHeight)+"px", 'top', nTable.style );
613 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
617 /* In the middle of the table */
620 this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
621 this._fnUpdateCache( oCache, 'sTop', oWin.iScrollTop+"px", 'top', nTable.style );
622 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
626 this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
627 this._fnUpdateCache( oCache, 'sTop', s.oOffset.top+"px", 'top', nTable.style );
628 this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft-oWin.iScrollLeft)+"px", 'left', nTable.style );
634 * Function: _fnUpdateCache
635 * Purpose: Check the cache and update cache and value if needed
637 * Inputs: object:oCache - local cache object
638 * string:sCache - cache property
639 * string:sSet - value to set
640 * string:sProperty - object property to set
641 * object:oObj - object to update
643 _fnUpdateCache: function ( oCache, sCache, sSet, sProperty, oObj )
645 if ( oCache[sCache] != sSet )
647 oObj[sProperty] = sSet;
648 oCache[sCache] = sSet;
654 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
659 * Function: _fnCloneThead
660 * Purpose: Clone the thead element
662 * Inputs: object:oCache - the cahced values for this fixed element
664 _fnCloneThead: function ( oCache )
666 var s = this.fnGetSettings();
667 var nTable = oCache.nNode;
669 /* Set the wrapper width to match that of the cloned table */
670 oCache.nWrapper.style.width = jQuery(s.nTable).outerWidth()+"px";
672 /* Remove any children the cloned table has */
673 while ( nTable.childNodes.length > 0 )
675 jQuery('thead th', nTable).unbind( 'click' );
676 nTable.removeChild( nTable.childNodes[0] );
679 /* Clone the DataTables header */
680 var nThead = jQuery('thead', s.nTable).clone(true)[0];
681 nTable.appendChild( nThead );
683 /* Copy the widths across - apparently a clone isn't good enough for this */
684 jQuery("thead>tr th", s.nTable).each( function (i) {
685 jQuery("thead>tr th:eq("+i+")", nTable).width( jQuery(this).width() );
688 jQuery("thead>tr td", s.nTable).each( function (i) {
689 jQuery("thead>tr td:eq("+i+")", nTable).width( jQuery(this).width() );
694 * Function: _fnCloneTfoot
695 * Purpose: Clone the tfoot element
697 * Inputs: object:oCache - the cahced values for this fixed element
699 _fnCloneTfoot: function ( oCache )
701 var s = this.fnGetSettings();
702 var nTable = oCache.nNode;
704 /* Set the wrapper width to match that of the cloned table */
705 oCache.nWrapper.style.width = jQuery(s.nTable).outerWidth()+"px";
707 /* Remove any children the cloned table has */
708 while ( nTable.childNodes.length > 0 )
710 nTable.removeChild( nTable.childNodes[0] );
713 /* Clone the DataTables footer */
714 var nTfoot = jQuery('tfoot', s.nTable).clone(true)[0];
715 nTable.appendChild( nTfoot );
717 /* Copy the widths across - apparently a clone isn't good enough for this */
718 jQuery("tfoot:eq(0)>tr th", s.nTable).each( function (i) {
719 jQuery("tfoot:eq(0)>tr th:eq("+i+")", nTable).width( jQuery(this).width() );
722 jQuery("tfoot:eq(0)>tr td", s.nTable).each( function (i) {
723 jQuery("tfoot:eq(0)>tr th:eq("+i+")", nTable)[0].style.width( jQuery(this).width() );
728 * Function: _fnCloneTLeft
729 * Purpose: Clone the left column
731 * Inputs: object:oCache - the cached values for this fixed element
733 _fnCloneTLeft: function ( oCache )
735 var s = this.fnGetSettings();
736 var nTable = oCache.nNode;
737 var nBody = $('tbody', s.nTable)[0];
738 var iCols = $('tbody tr:eq(0) td', s.nTable).length;
739 var bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"));
741 /* Remove any children the cloned table has */
742 while ( nTable.childNodes.length > 0 )
744 nTable.removeChild( nTable.childNodes[0] );
747 /* Is this the most efficient way to do this - it looks horrible... */
748 nTable.appendChild( jQuery("thead", s.nTable).clone(true)[0] );
749 nTable.appendChild( jQuery("tbody", s.nTable).clone(true)[0] );
752 nTable.appendChild( jQuery("tfoot", s.nTable).clone(true)[0] );
755 /* Remove unneeded cells */
756 $('thead tr', nTable).each( function (k) {
757 $('th:gt(0)', this).remove();
760 $('tfoot tr', nTable).each( function (k) {
761 $('th:gt(0)', this).remove();
764 $('tbody tr', nTable).each( function (k) {
765 $('td:gt(0)', this).remove();
768 this.fnEqualiseHeights( 'tbody', nBody.parentNode, nTable );
770 var iWidth = jQuery('thead tr th:eq(0)', s.nTable).outerWidth();
771 nTable.style.width = iWidth+"px";
772 oCache.nWrapper.style.width = iWidth+"px";
776 * Function: _fnCloneTRight
777 * Purpose: Clone the right most colun
779 * Inputs: object:oCache - the cahced values for this fixed element
781 _fnCloneTRight: function ( oCache )
783 var s = this.fnGetSettings();
784 var nBody = $('tbody', s.nTable)[0];
785 var nTable = oCache.nNode;
786 var iCols = jQuery('tbody tr:eq(0) td', s.nTable).length;
787 var bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"));
789 /* Remove any children the cloned table has */
790 while ( nTable.childNodes.length > 0 )
792 nTable.removeChild( nTable.childNodes[0] );
795 /* Is this the most efficient way to do this - it looks horrible... */
796 nTable.appendChild( jQuery("thead", s.nTable).clone(true)[0] );
797 nTable.appendChild( jQuery("tbody", s.nTable).clone(true)[0] );
800 nTable.appendChild( jQuery("tfoot", s.nTable).clone(true)[0] );
802 jQuery('thead tr th:not(:nth-child('+iCols+'n))', nTable).remove();
803 jQuery('tfoot tr th:not(:nth-child('+iCols+'n))', nTable).remove();
805 /* Remove unneeded cells */
806 $('tbody tr', nTable).each( function (k) {
807 $('td:lt('+(iCols-1)+')', this).remove();
810 this.fnEqualiseHeights( 'tbody', nBody.parentNode, nTable );
812 var iWidth = jQuery('thead tr th:eq('+(iCols-1)+')', s.nTable).outerWidth();
813 nTable.style.width = iWidth+"px";
814 oCache.nWrapper.style.width = iWidth+"px";
819 * Equalise the heights of the rows in a given table node in a cross browser way. Note that this
820 * is more or less lifted as is from FixedColumns
821 * @method fnEqualiseHeights
823 * @param {string} parent Node type - thead, tbody or tfoot
824 * @param {element} original Original node to take the heights from
825 * @param {element} clone Copy the heights to
828 "fnEqualiseHeights": function ( parent, original, clone )
831 jqBoxHack = $(parent+' tr:eq(0)', original).children(':eq(0)'),
832 iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height(),
833 bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"));
835 /* Remove cells which are not needed and copy the height from the original table */
836 $(parent+' tr', clone).each( function (k) {
837 /* Can we use some kind of object detection here?! This is very nasty - damn browsers */
838 if ( $.browser.mozilla || $.browser.opera )
840 $(this).children().height( $(parent+' tr:eq('+k+')', original).outerHeight() );
844 $(this).children().height( $(parent+' tr:eq('+k+')', original).outerHeight() - iBoxHack );
847 if ( !bRubbishOldIE )
849 $(parent+' tr:eq('+k+')', original).height( $(parent+' tr:eq('+k+')', original).outerHeight() );
856 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
857 * Static properties and methods
858 * We use these for speed! This information is common to all instances of FixedHeader, so no
859 * point if having them calculated and stored for each different instance.
864 * Purpose: Store information about the window positioning
878 * Purpose: Store information about the document size
887 * Variable: afnScroll
888 * Purpose: Array of functions that are to be used for the scrolling components
891 FixedHeader.afnScroll = [];
894 * Function: fnMeasure
895 * Purpose: Update the measurements for the window and document
899 FixedHeader.fnMeasure = function ()
902 jqWin = jQuery(window),
903 jqDoc = jQuery(document),
904 oWin = FixedHeader.oWin,
905 oDoc = FixedHeader.oDoc;
907 oDoc.iHeight = jqDoc.height();
908 oDoc.iWidth = jqDoc.width();
910 oWin.iHeight = jqWin.height();
911 oWin.iWidth = jqWin.width();
912 oWin.iScrollTop = jqWin.scrollTop();
913 oWin.iScrollLeft = jqWin.scrollLeft();
914 oWin.iScrollRight = oDoc.iWidth - oWin.iScrollLeft - oWin.iWidth;
915 oWin.iScrollBottom = oDoc.iHeight - oWin.iScrollTop - oWin.iHeight;
919 FixedHeader.VERSION = "2.0.6";
920 FixedHeader.prototype.VERSION = FixedHeader.VERSION;
923 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
928 * Just one 'scroll' event handler in FixedHeader, which calls the required components. This is
929 * done as an optimisation, to reduce calculation and proagation time
931 jQuery(window).scroll( function () {
932 FixedHeader.fnMeasure();
933 for ( var i=0, iLen=FixedHeader.afnScroll.length ; i<iLen ; i++ )
935 FixedHeader.afnScroll[i]();