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-2010 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" */
74 * Function: fnGetSettings
75 * Purpose: Get the settings for this object
76 * Returns: object: - settings object
79 this.fnGetSettings = function () {
85 * Purpose: Update the positioning and copies of the fixed elements
89 this.fnUpdate = function () {
90 this._fnUpdateClones();
91 this._fnUpdatePositions();
95 * Function: fnPosition
96 * Purpose: Update the positioning of the fixed elements
100 this.fnPosition = function () {
101 this._fnUpdatePositions();
105 this.fnInit( mTable, oInit );
107 /* Store the instance on the DataTables object for easy access */
108 if ( typeof mTable.fnSettings == 'function' )
110 mTable._oPluginFixedHeader = this;
116 * Variable: FixedHeader
117 * Purpose: Prototype for FixedHeader
120 FixedHeader.prototype = {
121 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
127 * Purpose: The "constructor"
129 * Inputs: {as FixedHeader function}
131 fnInit: function ( oTable, oInit )
133 var s = this.fnGetSettings();
136 /* Record the user definable settings */
137 this.fnInitSettings( s, oInit );
139 /* DataTables specific stuff */
140 if ( typeof oTable.fnSettings == 'function' )
142 if ( typeof oTable.fnVersionCheck == 'functon' &&
143 oTable.fnVersionCheck( '1.6.0' ) !== true )
145 alert( "FixedHeader 2 required DataTables 1.6.0 or later. "+
146 "Please upgrade your DataTables installation" );
150 var oDtSettings = oTable.fnSettings();
152 if ( oDtSettings.oScroll.sX != "" || oDtSettings.oScroll.sY != "" )
154 alert( "FixedHeader 2 is not supported with DataTables' scrolling mode at this time" );
158 s.nTable = oDtSettings.nTable;
159 oDtSettings.aoDrawCallback.push( {
161 FixedHeader.fnMeasure();
162 that._fnUpdateClones.call(that);
163 that._fnUpdatePositions.call(that);
165 "sName": "FixedHeader"
173 s.bFooter = ($('>tfoot', s.nTable).length > 0) ? true : false;
175 /* "Detect" browsers that don't support absolute positioing - or have bugs */
176 s.bUseAbsPos = (jQuery.browser.msie && (jQuery.browser.version=="6.0"||jQuery.browser.version=="7.0"));
178 /* Add the 'sides' that are fixed */
181 s.aoCache.push( that._fnCloneTable( "fixedHeader", "FixedHeader_Header", that._fnCloneThead ) );
183 if ( s.oSides.bottom )
185 s.aoCache.push( that._fnCloneTable( "fixedFooter", "FixedHeader_Footer", that._fnCloneTfoot ) );
189 s.aoCache.push( that._fnCloneTable( "fixedLeft", "FixedHeader_Left", that._fnCloneTLeft ) );
191 if ( s.oSides.right )
193 s.aoCache.push( that._fnCloneTable( "fixedRight", "FixedHeader_Right", that._fnCloneTRight ) );
196 /* Event listeners for window movement */
197 FixedHeader.afnScroll.push( function () {
198 that._fnUpdatePositions.call(that);
201 jQuery(window).resize( function () {
202 FixedHeader.fnMeasure();
203 that._fnUpdateClones.call(that);
204 that._fnUpdatePositions.call(that);
207 /* Get things right to start with */
208 FixedHeader.fnMeasure();
209 that._fnUpdateClones();
210 that._fnUpdatePositions();
214 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
219 * Function: fnInitSettings
220 * Purpose: Take the user's settings and copy them to our local store
222 * Inputs: object:s - the local settings object
223 * object:oInit - the user's settings object
225 fnInitSettings: function ( s, oInit )
227 if ( typeof oInit != 'undefined' )
229 if ( typeof oInit.top != 'undefined' ) {
230 s.oSides.top = oInit.top;
232 if ( typeof oInit.bottom != 'undefined' ) {
233 s.oSides.bottom = oInit.bottom;
235 if ( typeof oInit.left != 'undefined' ) {
236 s.oSides.left = oInit.left;
238 if ( typeof oInit.right != 'undefined' ) {
239 s.oSides.right = oInit.right;
242 if ( typeof oInit.zTop != 'undefined' ) {
243 s.oZIndexes.top = oInit.zTop;
245 if ( typeof oInit.zBottom != 'undefined' ) {
246 s.oZIndexes.bottom = oInit.zBottom;
248 if ( typeof oInit.zLeft != 'undefined' ) {
249 s.oZIndexes.left = oInit.zLeft;
251 if ( typeof oInit.zRight != 'undefined' ) {
252 s.oZIndexes.right = oInit.zRight;
256 /* Detect browsers which have poor position:fixed support so we can use absolute positions.
257 * This is much slower since the position must be updated for each scroll, but widens
260 s.bUseAbsPos = (jQuery.browser.msie &&
261 (jQuery.browser.version=="6.0"||jQuery.browser.version=="7.0"));
265 * Function: _fnCloneTable
266 * Purpose: Clone the table node and do basic initialisation
270 _fnCloneTable: function ( sType, sClass, fnClone )
272 var s = this.fnGetSettings();
275 /* We know that the table _MUST_ has a DIV wrapped around it, because this is simply how
276 * DataTables works. Therefore, we can set this to be relatively position (if it is not
277 * alreadu absolute, and use this as the base point for the cloned header
279 if ( jQuery(s.nTable.parentNode).css('position') != "absolute" )
281 s.nTable.parentNode.style.position = "relative";
284 /* Just a shallow clone will do - we only want the table node */
285 nCTable = s.nTable.cloneNode( false );
286 nCTable.removeAttribute( 'id' );
288 var nDiv = document.createElement( 'div' );
289 nDiv.style.position = "absolute";
290 nDiv.style.top = "0px";
291 nDiv.style.left = "0px";
292 nDiv.className += " FixedHeader_Cloned "+sType+" "+sClass;
294 /* Set the zIndexes */
295 if ( sType == "fixedHeader" )
297 nDiv.style.zIndex = s.oZIndexes.top;
299 if ( sType == "fixedFooter" )
301 nDiv.style.zIndex = s.oZIndexes.bottom;
303 if ( sType == "fixedLeft" )
305 nDiv.style.zIndex = s.oZIndexes.left;
307 else if ( sType == "fixedRight" )
309 nDiv.style.zIndex = s.oZIndexes.right;
312 /* remove margins since we are going to poistion it absolutely */
313 nCTable.style.margin = "0";
315 /* Insert the newly cloned table into the DOM, on top of the "real" header */
316 nDiv.appendChild( nCTable );
317 document.body.appendChild( nDiv );
331 * Function: _fnUpdatePositions
332 * Purpose: Get the current positioning of the table in the DOM
336 _fnMeasure: function ()
339 s = this.fnGetSettings(),
341 jqTable = jQuery(s.nTable),
342 oOffset = jqTable.offset(),
343 iParentScrollTop = this._fnSumScroll( s.nTable.parentNode, 'scrollTop' ),
344 iParentScrollLeft = this._fnSumScroll( s.nTable.parentNode, 'scrollLeft' );
346 m.iTableWidth = jqTable.outerWidth();
347 m.iTableHeight = jqTable.outerHeight();
348 m.iTableLeft = oOffset.left + s.nTable.parentNode.scrollLeft;
349 m.iTableTop = oOffset.top + iParentScrollTop;
350 m.iTableRight = m.iTableLeft + m.iTableWidth;
351 m.iTableRight = FixedHeader.oDoc.iWidth - m.iTableLeft - m.iTableWidth;
352 m.iTableBottom = FixedHeader.oDoc.iHeight - m.iTableTop - m.iTableHeight;
356 * Function: _fnSumScroll
357 * Purpose: Sum node parameters all the way to the top
359 * Inputs: node:n - node to consider
360 * string:side - scrollTop or scrollLeft
362 _fnSumScroll: function ( n, side )
365 while ( n = n.parentNode )
367 if ( n.nodeName != 'HTML' && n.nodeName != 'BODY' )
377 * Function: _fnUpdatePositions
378 * Purpose: Loop over the fixed elements for this table and update their positions
382 _fnUpdatePositions: function ()
384 var s = this.fnGetSettings();
387 for ( var i=0, iLen=s.aoCache.length ; i<iLen ; i++ )
389 if ( s.aoCache[i].sType == "fixedHeader" )
391 this._fnScrollFixedHeader( s.aoCache[i] );
393 else if ( s.aoCache[i].sType == "fixedFooter" )
395 this._fnScrollFixedFooter( s.aoCache[i] );
397 else if ( s.aoCache[i].sType == "fixedLeft" )
399 this._fnScrollHorizontalLeft( s.aoCache[i] );
403 this._fnScrollHorizontalRight( s.aoCache[i] );
409 * Function: _fnUpdateClones
410 * Purpose: Loop over the fixed elements for this table and call their cloning functions
414 _fnUpdateClones: function ()
416 var s = this.fnGetSettings();
417 for ( var i=0, iLen=s.aoCache.length ; i<iLen ; i++ )
419 s.aoCache[i].fnClone.call( this, s.aoCache[i] );
424 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
425 * Scrolling functions
429 * Function: _fnScrollHorizontalLeft
430 * Purpose: Update the positioning of the scrolling elements
432 * Inputs: object:oCache - the cahced values for this fixed element
434 _fnScrollHorizontalRight: function ( oCache )
437 s = this.fnGetSettings(),
439 oWin = FixedHeader.oWin,
440 oDoc = FixedHeader.oDoc,
441 nTable = oCache.nWrapper,
442 iFixedWidth = jQuery(nTable).outerWidth();
444 if ( oWin.iScrollRight < oMes.iTableRight )
446 /* Fully right aligned */
447 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
448 this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
449 this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft+oMes.iTableWidth-iFixedWidth)+"px", 'left', nTable.style );
451 else if ( oMes.iTableLeft < oDoc.iWidth-oWin.iScrollRight-iFixedWidth )
456 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
457 this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
458 this._fnUpdateCache( oCache, 'sLeft', (oDoc.iWidth-oWin.iScrollRight-iFixedWidth)+"px", 'left', nTable.style );
462 this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
463 this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop-oWin.iScrollTop)+"px", 'top', nTable.style );
464 this._fnUpdateCache( oCache, 'sLeft', (oWin.iWidth-iFixedWidth)+"px", 'left', nTable.style );
469 /* Fully left aligned */
470 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
471 this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
472 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
477 * Function: _fnScrollHorizontalLeft
478 * Purpose: Update the positioning of the scrolling elements
480 * Inputs: object:oCache - the cahced values for this fixed element
482 _fnScrollHorizontalLeft: function ( oCache )
485 s = this.fnGetSettings(),
487 oWin = FixedHeader.oWin,
488 oDoc = FixedHeader.oDoc,
489 nTable = oCache.nWrapper,
490 iCellWidth = jQuery(nTable).outerWidth();
492 if ( oWin.iScrollLeft < oMes.iTableLeft )
494 /* Fully left align */
495 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
496 this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
497 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
499 else if ( oWin.iScrollLeft < oMes.iTableLeft+oMes.iTableWidth-iCellWidth )
504 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
505 this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
506 this._fnUpdateCache( oCache, 'sLeft', oWin.iScrollLeft+"px", 'left', nTable.style );
510 this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
511 this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop-oWin.iScrollTop)+"px", 'top', nTable.style );
512 this._fnUpdateCache( oCache, 'sLeft', "0px", 'left', nTable.style );
517 /* Fully right align */
518 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
519 this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
520 this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft+oMes.iTableWidth-iCellWidth)+"px", 'left', nTable.style );
525 * Function: _fnScrollFixedFooter
526 * Purpose: Update the positioning of the scrolling elements
528 * Inputs: object:oCache - the cahced values for this fixed element
530 _fnScrollFixedFooter: function ( oCache )
533 s = this.fnGetSettings(),
535 oWin = FixedHeader.oWin,
536 oDoc = FixedHeader.oDoc,
537 nTable = oCache.nWrapper,
538 iTheadHeight = jQuery("thead", s.nTable).outerHeight(),
539 iCellHeight = jQuery(nTable).outerHeight();
541 if ( oWin.iScrollBottom < oMes.iTableBottom )
544 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
545 this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+oMes.iTableHeight-iCellHeight)+"px", 'top', nTable.style );
546 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
548 else if ( oWin.iScrollBottom < oMes.iTableBottom+oMes.iTableHeight-iCellHeight-iTheadHeight )
553 this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
554 this._fnUpdateCache( oCache, 'sTop', (oDoc.iHeight-oWin.iScrollBottom-iCellHeight)+"px", 'top', nTable.style );
555 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
559 this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
560 this._fnUpdateCache( oCache, 'sTop', (oWin.iHeight-iCellHeight)+"px", 'top', nTable.style );
561 this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft-oWin.iScrollLeft)+"px", 'left', nTable.style );
567 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
568 this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+iCellHeight)+"px", 'top', nTable.style );
569 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
574 * Function: _fnScrollFixedHeader
575 * Purpose: Update the positioning of the scrolling elements
577 * Inputs: object:oCache - the cahced values for this fixed element
579 _fnScrollFixedHeader: function ( oCache )
582 s = this.fnGetSettings(),
584 oWin = FixedHeader.oWin,
585 oDoc = FixedHeader.oDoc,
586 nTable = oCache.nWrapper,
587 iTbodyHeight = s.nTable.getElementsByTagName('tbody')[0].offsetHeight;
589 if ( oMes.iTableTop > oWin.iScrollTop )
591 /* Above the table */
592 this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
593 this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
594 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
596 else if ( oWin.iScrollTop > oMes.iTableTop+iTbodyHeight )
598 /* At the bottom of the table */
599 this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
600 this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+iTbodyHeight)+"px", 'top', nTable.style );
601 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
605 /* In the middle of the table */
608 this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
609 this._fnUpdateCache( oCache, 'sTop', oWin.iScrollTop+"px", 'top', nTable.style );
610 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
614 this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
615 this._fnUpdateCache( oCache, 'sTop', "0px", 'top', nTable.style );
616 this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft-oWin.iScrollLeft)+"px", 'left', nTable.style );
622 * Function: _fnUpdateCache
623 * Purpose: Check the cache and update cache and value if needed
625 * Inputs: object:oCache - local cache object
626 * string:sCache - cache property
627 * string:sSet - value to set
628 * string:sProperty - object property to set
629 * object:oObj - object to update
631 _fnUpdateCache: function ( oCache, sCache, sSet, sProperty, oObj )
633 if ( oCache[sCache] != sSet )
635 oObj[sProperty] = sSet;
636 oCache[sCache] = sSet;
642 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
647 * Function: _fnCloneThead
648 * Purpose: Clone the thead element
650 * Inputs: object:oCache - the cahced values for this fixed element
652 _fnCloneThead: function ( oCache )
654 var s = this.fnGetSettings();
655 var nTable = oCache.nNode;
657 /* Set the wrapper width to match that of the cloned table */
658 oCache.nWrapper.style.width = jQuery(s.nTable).outerWidth()+"px";
660 /* Remove any children the cloned table has */
661 while ( nTable.childNodes.length > 0 )
663 jQuery('thead th', nTable).unbind( 'click' );
664 nTable.removeChild( nTable.childNodes[0] );
667 /* Clone the DataTables header */
668 var nThead = jQuery('thead', s.nTable).clone(true)[0];
669 nTable.appendChild( nThead );
671 /* Copy the widths across - apparently a clone isn't good enough for this */
672 jQuery("thead>tr th", s.nTable).each( function (i) {
673 jQuery("thead>tr th:eq("+i+")", nTable).width( jQuery(this).width() );
676 jQuery("thead>tr td", s.nTable).each( function (i) {
677 jQuery("thead>tr td:eq("+i+")", nTable).width( jQuery(this).width() );
682 * Function: _fnCloneTfoot
683 * Purpose: Clone the tfoot element
685 * Inputs: object:oCache - the cahced values for this fixed element
687 _fnCloneTfoot: function ( oCache )
689 var s = this.fnGetSettings();
690 var nTable = oCache.nNode;
692 /* Set the wrapper width to match that of the cloned table */
693 oCache.nWrapper.style.width = jQuery(s.nTable).outerWidth()+"px";
695 /* Remove any children the cloned table has */
696 while ( nTable.childNodes.length > 0 )
698 nTable.removeChild( nTable.childNodes[0] );
701 /* Clone the DataTables footer */
702 var nTfoot = jQuery('tfoot', s.nTable).clone(true)[0];
703 nTable.appendChild( nTfoot );
705 /* Copy the widths across - apparently a clone isn't good enough for this */
706 jQuery("tfoot:eq(0)>tr th", s.nTable).each( function (i) {
707 jQuery("tfoot:eq(0)>tr th:eq("+i+")", nTable).width( jQuery(this).width() );
710 jQuery("tfoot:eq(0)>tr td", s.nTable).each( function (i) {
711 jQuery("tfoot:eq(0)>tr th:eq("+i+")", nTable)[0].style.width( jQuery(this).width() );
716 * Function: _fnCloneTLeft
717 * Purpose: Clone the left column
719 * Inputs: object:oCache - the cahced values for this fixed element
721 _fnCloneTLeft: function ( oCache )
723 var s = this.fnGetSettings();
724 var nTable = oCache.nNode;
725 var nBody = $('tbody', s.nTable)[0];
726 var iCols = $('tbody tr:eq(0) td', s.nTable).length;
727 var bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"));
729 /* Remove any children the cloned table has */
730 while ( nTable.childNodes.length > 0 )
732 nTable.removeChild( nTable.childNodes[0] );
735 /* Is this the most efficient way to do this - it looks horrible... */
736 nTable.appendChild( jQuery("thead", s.nTable).clone(true)[0] );
737 nTable.appendChild( jQuery("tbody", s.nTable).clone(true)[0] );
740 nTable.appendChild( jQuery("tfoot", s.nTable).clone(true)[0] );
743 jQuery('thead tr th:gt(0)', nTable).remove();
744 jQuery('tfoot tr th:gt(0)', nTable).remove();
746 /* Remove unneeded cells */
747 $('tbody tr', nTable).each( function (k) {
748 $('td:gt(0)', this).remove();
751 this.fnEqualiseHeights( 'tbody', nBody.parentNode, nTable );
753 var iWidth = jQuery('thead tr th:eq(0)', s.nTable).outerWidth();
754 nTable.style.width = iWidth+"px";
755 oCache.nWrapper.style.width = iWidth+"px";
759 * Function: _fnCloneTRight
760 * Purpose: Clone the right most colun
762 * Inputs: object:oCache - the cahced values for this fixed element
764 _fnCloneTRight: function ( oCache )
766 var s = this.fnGetSettings();
767 var nBody = $('tbody', s.nTable)[0];
768 var nTable = oCache.nNode;
769 var iCols = jQuery('tbody tr:eq(0) td', s.nTable).length;
770 var bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"));
772 /* Remove any children the cloned table has */
773 while ( nTable.childNodes.length > 0 )
775 nTable.removeChild( nTable.childNodes[0] );
778 /* Is this the most efficient way to do this - it looks horrible... */
779 nTable.appendChild( jQuery("thead", s.nTable).clone(true)[0] );
780 nTable.appendChild( jQuery("tbody", s.nTable).clone(true)[0] );
783 nTable.appendChild( jQuery("tfoot", s.nTable).clone(true)[0] );
785 jQuery('thead tr th:not(:nth-child('+iCols+'n))', nTable).remove();
786 jQuery('tfoot tr th:not(:nth-child('+iCols+'n))', nTable).remove();
788 /* Remove unneeded cells */
789 $('tbody tr', nTable).each( function (k) {
790 $('td:lt('+(iCols-1)+')', this).remove();
793 this.fnEqualiseHeights( 'tbody', nBody.parentNode, nTable );
795 var iWidth = jQuery('thead tr th:eq('+(iCols-1)+')', s.nTable).outerWidth();
796 nTable.style.width = iWidth+"px";
797 oCache.nWrapper.style.width = iWidth+"px";
802 * Equalise the heights of the rows in a given table node in a cross browser way. Note that this
803 * is more or less lifted as is from FixedColumns
804 * @method fnEqualiseHeights
806 * @param {string} parent Node type - thead, tbody or tfoot
807 * @param {element} original Original node to take the heights from
808 * @param {element} clone Copy the heights to
811 "fnEqualiseHeights": function ( parent, original, clone )
814 jqBoxHack = $(parent+' tr:eq(0)', original).children(':eq(0)'),
815 iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height(),
816 bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"));
818 /* Remove cells which are not needed and copy the height from the original table */
819 $(parent+' tr', clone).each( function (k) {
820 /* Can we use some kind of object detection here?! This is very nasty - damn browsers */
821 if ( $.browser.mozilla || $.browser.opera )
823 $(this).children().height( $(parent+' tr:eq('+k+')', original).outerHeight() );
827 $(this).children().height( $(parent+' tr:eq('+k+')', original).outerHeight() - iBoxHack );
830 if ( !bRubbishOldIE )
832 $(parent+' tr:eq('+k+')', original).height( $(parent+' tr:eq('+k+')', original).outerHeight() );
839 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
840 * Static properties and methods
841 * We use these for speed! This information is common to all instances of FixedHeader, so no
842 * point if having them calculated and stored for each different instance.
847 * Purpose: Store information about the window positioning
861 * Purpose: Store information about the document size
870 * Variable: afnScroll
871 * Purpose: Array of functions that are to be used for the scrolling components
874 FixedHeader.afnScroll = [];
877 * Function: fnMeasure
878 * Purpose: Update the measurements for the window and document
882 FixedHeader.fnMeasure = function ()
885 jqWin = jQuery(window),
886 jqDoc = jQuery(document),
887 oWin = FixedHeader.oWin,
888 oDoc = FixedHeader.oDoc;
890 oDoc.iHeight = jqDoc.height();
891 oDoc.iWidth = jqDoc.width();
893 oWin.iHeight = jqWin.height();
894 oWin.iWidth = jqWin.width();
895 oWin.iScrollTop = jqWin.scrollTop();
896 oWin.iScrollLeft = jqWin.scrollLeft();
897 oWin.iScrollRight = oDoc.iWidth - oWin.iScrollLeft - oWin.iWidth;
898 oWin.iScrollBottom = oDoc.iHeight - oWin.iScrollTop - oWin.iHeight;
902 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
907 * Just one 'scroll' event handler in FixedHeader, which calls the required components. This is
908 * done as an optimisation, to reduce calculation and proagation time
910 jQuery(window).scroll( function () {
911 FixedHeader.fnMeasure();
912 for ( var i=0, iLen=FixedHeader.afnScroll.length ; i<iLen ; i++ )
914 FixedHeader.afnScroll[i]();