2 * @summary FixedColumns
3 * @description Freeze columns in place on a scrolling DataTable
4 * @file FixedColumns.js
6 * @author Allan Jardine (www.sprymedia.co.uk)
7 * @license GPL v2 or BSD 3 point style
8 * @contact www.sprymedia.co.uk/contact
10 * @copyright Copyright 2010-2011 Allan Jardine, all rights reserved.
12 * This source file is free software, under either the GPL v2 license or a
13 * BSD style license, available at:
14 * http://datatables.net/license_gpl2
15 * http://datatables.net/license_bsd
19 /* Global scope for FixedColumns */
22 (function($, window, document) {
26 * When making use of DataTables' x-axis scrolling feature, you may wish to
27 * fix the left most column in place. This plug-in for DataTables provides
28 * exactly this option (note for non-scrolling tables, please use the
29 * FixedHeader plug-in, which can fix headers, footers and columns). Key
31 * <ul class="limit_length">
32 * <li>Freezes the left or right most columns to the side of the table</li>
33 * <li>Option to freeze two or more columns</li>
34 * <li>Full integration with DataTables' scrolling options</li>
35 * <li>Speed - FixedColumns is fast in its operation</li>
40 * @param {object} oDT DataTables instance
41 * @param {object} [oInit={}] Configuration object for FixedColumns. Options are defined by {@link FixedColumns.defaults}
43 * @requires jQuery 1.3+
44 * @requires DataTables 1.8.0+
47 * var oTable = $('#example').dataTable( {
50 * new FixedColumns( oTable );
52 FixedColumns = function ( oDT, oInit ) {
53 /* Sanity check - you just know it will happen */
54 if ( ! this instanceof FixedColumns )
56 alert( "FixedColumns warning: FixedColumns must be initialised with the 'new' keyword." );
60 if ( typeof oInit == 'undefined' )
66 * Settings object which contains customisable information for FixedColumns instance
68 * @extends FixedColumns.defaults
72 * DataTables settings objects
74 * @default Obtained from DataTables instance
76 "dt": oDT.fnSettings(),
79 * Number of columns in the DataTable - stored for quick access
81 * @default Obtained from DataTables instance
83 "iTableColumns": oDT.fnSettings().aoColumns.length,
86 * Original widths of the columns as rendered by DataTables
93 * Flag to indicate if we are dealing with IE6/7 as these browsers need a little hack
96 * @default Automatically calculated
99 "bOldIE": ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"))
104 * DOM elements used by the class instance
110 * DataTables scrolling element
117 * DataTables header table
124 * DataTables body table
131 * DataTables footer table
138 * Display grid elements
143 * Grid wrapper. This is the container element for the 3x3 grid
150 * DataTables scrolling element. This element is the DataTables
151 * component in the display grid (making up the main table - i.e.
152 * not the fixed columns).
159 * Left fixed column grid components
170 * Right fixed column grid components
187 * Left column cloned table nodes
192 * Cloned header table
206 * Cloned footer table
214 * Right column cloned table nodes
219 * Cloned header table
233 * Cloned footer table
242 /* Attach the instance to the DataTables instance so it can be accessed easily */
243 this.s.dt.oFixedColumns = this;
246 this._fnConstruct( oInit );
251 FixedColumns.prototype = {
252 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
254 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
257 * Update the fixed columns - including headers and footers. Note that FixedColumns will
258 * automatically update the display whenever the host DataTable redraws.
261 * var oTable = $('#example').dataTable( {
264 * var oFC = new FixedColumns( oTable );
266 * // at some later point when the table has been manipulated....
269 "fnUpdate": function ()
271 this._fnDraw( true );
276 * Recalculate the resizes of the 3x3 grid that FixedColumns uses for display of the table.
277 * This is useful if you update the width of the table container. Note that FixedColumns will
278 * perform this function automatically when the window.resize event is fired.
281 * var oTable = $('#example').dataTable( {
284 * var oFC = new FixedColumns( oTable );
286 * // Resize the table container and then have FixedColumns adjust its layout....
287 * $('#content').width( 1200 );
288 * oFC.fnRedrawLayout();
290 "fnRedrawLayout": function ()
292 this._fnGridLayout();
297 * Mark a row such that it's height should be recalculated when using 'semiauto' row
298 * height matching. This function will have no effect when 'none' or 'auto' row height
300 * @param {Node} nTr TR element that should have it's height recalculated
303 * var oTable = $('#example').dataTable( {
306 * var oFC = new FixedColumns( oTable );
308 * // manipulate the table - mark the row as needing an update then update the table
309 * // this allows the redraw performed by DataTables fnUpdate to recalculate the row
311 * oFC.fnRecalculateHeight();
312 * oTable.fnUpdate( $('#example tbody tr:eq(0)')[0], ["insert date", 1, 2, 3 ... ]);
314 "fnRecalculateHeight": function ( nTr )
316 nTr._DTTC_iHeight = null;
317 nTr.style.height = 'auto';
322 * Set the height of a given row - provides cross browser compatibility
323 * @param {Node} nTarget TR element that should have it's height recalculated
324 * @param {int} iHeight Height in pixels to set
327 * var oTable = $('#example').dataTable( {
330 * var oFC = new FixedColumns( oTable );
332 * // You may want to do this after manipulating a row in the fixed column
333 * oFC.fnSetRowHeight( $('#example tbody tr:eq(0)')[0], 50 );
335 "fnSetRowHeight": function ( nTarget, iHeight )
337 var jqBoxHack = $(nTarget).children(':first');
338 var iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height();
340 /* Can we use some kind of object detection here?! This is very nasty - damn browsers */
341 if ( $.browser.mozilla || $.browser.opera )
343 nTarget.style.height = iHeight+"px";
347 $(nTarget).children().height( iHeight-iBoxHack );
353 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
354 * Private methods (they are of course public in JS, but recommended as private)
355 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
358 * Initialisation for FixedColumns
359 * @param {Object} oInit User settings for initialisation
363 "_fnConstruct": function ( oInit )
368 /* Sanity checking */
369 if ( typeof this.s.dt.oInstance.fnVersionCheck != 'function' ||
370 this.s.dt.oInstance.fnVersionCheck( '1.8.0' ) !== true )
372 alert( "FixedColumns "+FixedColumns.VERSION+" required DataTables 1.8.0 or later. "+
373 "Please upgrade your DataTables installation" );
377 if ( this.s.dt.oScroll.sX === "" )
379 this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "FixedColumns is not needed (no "+
380 "x-scrolling in DataTables enabled), so no action will be taken. Use 'FixedHeader' for "+
381 "column fixing when scrolling is not enabled" );
385 /* Apply the settings from the user / defaults */
386 this.s = $.extend( true, this.s, FixedColumns.defaults, oInit );
388 /* Set up the DOM as we need it and cache nodes */
389 this.dom.grid.dt = $(this.s.dt.nTable).parents('div.dataTables_scroll')[0];
390 this.dom.scroller = $('div.dataTables_scrollBody', this.dom.grid.dt )[0];
392 var iScrollWidth = $(this.dom.grid.dt).width();
396 $('tbody>tr:eq(0)>td', this.s.dt.nTable).each( function (i) {
397 iWidth = $(this).outerWidth();
398 that.s.aiWidths.push( iWidth );
399 if ( i < that.s.iLeftColumns )
401 iLeftWidth += iWidth;
403 if ( that.s.iTableColumns-that.s.iRightColumns <= i )
405 iRightWidth += iWidth;
409 if ( this.s.iLeftWidth === null )
411 this.s.iLeftWidth = this.s.sLeftWidth == 'fixed' ?
412 iLeftWidth : (iLeftWidth/iScrollWidth) * 100;
415 if ( this.s.iRightWidth === null )
417 this.s.iRightWidth = this.s.sRightWidth == 'fixed' ?
418 iRightWidth : (iRightWidth/iScrollWidth) * 100;
421 /* Set up the DOM that we want for the fixed column layout grid */
424 /* Use the DataTables API method fnSetColumnVis to hide the columns we are going to fix */
425 for ( i=0 ; i<this.s.iLeftColumns ; i++ )
427 this.s.dt.oInstance.fnSetColumnVis( i, false );
429 for ( i=this.s.iTableColumns - this.s.iRightColumns ; i<this.s.iTableColumns ; i++ )
431 this.s.dt.oInstance.fnSetColumnVis( i, false );
435 $(this.dom.scroller).scroll( function () {
436 that.dom.grid.left.body.scrollTop = that.dom.scroller.scrollTop;
437 if ( that.s.iRightColumns > 0 )
439 that.dom.grid.right.body.scrollTop = that.dom.scroller.scrollTop;
443 $(window).resize( function () {
444 that._fnGridLayout.call( that );
447 var bFirstDraw = true;
448 this.s.dt.aoDrawCallback = [ {
450 that._fnDraw.call( that, bFirstDraw );
451 that._fnGridHeight( that );
454 "sName": "FixedColumns"
455 } ].concat( this.s.dt.aoDrawCallback );
457 /* Get things right to start with - note that due to adjusting the columns, there must be
458 * another redraw of the main table. It doesn't need to be a full redraw however.
460 this._fnGridLayout();
461 this._fnGridHeight();
462 this.s.dt.oInstance.fnDraw(false);
467 * Set up the DOM for the fixed column. The way the layout works is to create a 1x3 grid
468 * for the left column, the DataTable (for which we just reuse the scrolling element DataTable
469 * puts into the DOM) and the right column. In each of he two fixed column elements there is a
470 * grouping wrapper element and then a head, body and footer wrapper. In each of these we then
471 * place the cloned header, body or footer tables. This effectively gives as 3x3 grid structure.
475 "_fnGridSetup": function ()
479 this.dom.body = this.s.dt.nTable;
480 this.dom.header = this.s.dt.nTHead.parentNode;
481 this.dom.header.parentNode.parentNode.style.position = "relative";
484 $('<div class="DTFC_ScrollWrapper" style="position:relative; clear:both;">'+
485 '<div class="DTFC_LeftWrapper" style="position:absolute; top:0; left:0;">'+
486 '<div class="DTFC_LeftHeadWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
487 '<div class="DTFC_LeftBodyWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
488 '<div class="DTFC_LeftFootWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
490 '<div class="DTFC_RightWrapper" style="position:absolute; top:0; left:0;">'+
491 '<div class="DTFC_RightHeadWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
492 '<div class="DTFC_RightBodyWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
493 '<div class="DTFC_RightFootWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
496 nLeft = nSWrapper.childNodes[0];
497 nRight = nSWrapper.childNodes[1];
499 this.dom.grid.wrapper = nSWrapper;
500 this.dom.grid.left.wrapper = nLeft;
501 this.dom.grid.left.head = nLeft.childNodes[0];
502 this.dom.grid.left.body = nLeft.childNodes[1];
504 if ( this.s.iRightColumns > 0 )
506 this.dom.grid.right.wrapper = nRight;
507 this.dom.grid.right.head = nRight.childNodes[0];
508 this.dom.grid.right.body = nRight.childNodes[1];
511 if ( this.s.dt.nTFoot )
513 this.dom.footer = this.s.dt.nTFoot.parentNode;
514 this.dom.grid.left.foot = nLeft.childNodes[2];
515 if ( this.s.iRightColumns > 0 )
517 this.dom.grid.right.foot = nRight.childNodes[2];
521 nSWrapper.appendChild( nLeft );
522 this.dom.grid.dt.parentNode.insertBefore( nSWrapper, this.dom.grid.dt );
523 nSWrapper.appendChild( this.dom.grid.dt );
525 this.dom.grid.dt.style.position = "absolute";
526 this.dom.grid.dt.style.top = "0px";
527 this.dom.grid.dt.style.left = this.s.iLeftWidth+"px";
528 this.dom.grid.dt.style.width = ($(this.dom.grid.dt).width()-this.s.iLeftWidth-this.s.iRightWidth)+"px";
533 * Style and position the grid used for the FixedColumns layout based on the instance settings.
534 * Specifically sLeftWidth ('fixed' or 'absolute'), iLeftWidth (px if fixed, % if absolute) and
535 * there 'right' counterparts.
539 "_fnGridLayout": function ()
541 var oGrid = this.dom.grid;
542 var iTotal = $(oGrid.wrapper).width();
543 var iLeft = 0, iRight = 0, iRemainder = 0;
545 if ( this.s.sLeftWidth == 'fixed' )
547 iLeft = this.s.iLeftWidth;
551 iLeft = ( this.s.iLeftWidth / 100 ) * iTotal;
554 if ( this.s.sRightWidth == 'fixed' )
556 iRight = this.s.iRightWidth;
560 iRight = ( this.s.iRightWidth / 100 ) * iTotal;
563 iRemainder = iTotal - iLeft - iRight;
565 oGrid.left.wrapper.style.width = iLeft+"px";
566 oGrid.dt.style.width = iRemainder+"px";
567 oGrid.dt.style.left = iLeft+"px";
569 if ( this.s.iRightColumns > 0 )
571 oGrid.right.wrapper.style.width = iRight+"px";
572 oGrid.right.wrapper.style.left = (iTotal-iRight)+"px";
578 * Recalculate and set the height of the grid components used for positioning of the
579 * FixedColumn display grid.
583 "_fnGridHeight": function ()
585 var oGrid = this.dom.grid;
586 var iHeight = $(this.dom.grid.dt).height();
588 oGrid.wrapper.style.height = iHeight+"px";
589 oGrid.left.body.style.height = $(this.dom.scroller).height()+"px";
590 oGrid.left.wrapper.style.height = iHeight+"px";
592 if ( this.s.iRightColumns > 0 )
594 oGrid.right.wrapper.style.height = iHeight+"px";
595 oGrid.right.body.style.height = $(this.dom.scroller).height()+"px";
601 * Clone and position the fixed columns
603 * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)
606 "_fnDraw": function ( bAll )
608 this._fnCloneLeft( bAll );
609 this._fnCloneRight( bAll );
611 /* Draw callback function */
612 if ( this.s.fnDrawCallback !== null )
614 this.s.fnDrawCallback.call( this, this.dom.clone.left, this.dom.clone.right );
617 /* Event triggering */
618 $(this).trigger( 'draw', {
619 "leftClone": this.dom.clone.left,
620 "rightClone": this.dom.clone.right
626 * Clone the right columns
628 * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)
631 "_fnCloneRight": function ( bAll )
633 if ( this.s.iRightColumns <= 0 )
642 for ( i=this.s.iTableColumns-this.s.iRightColumns ; i<this.s.iTableColumns ; i++ )
647 this._fnClone( this.dom.clone.right, this.dom.grid.right, aiColumns, bAll );
652 * Clone the left columns
654 * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)
657 "_fnCloneLeft": function ( bAll )
659 if ( this.s.iLeftColumns <= 0 )
668 for ( i=0 ; i<this.s.iLeftColumns ; i++ )
673 this._fnClone( this.dom.clone.left, this.dom.grid.left, aiColumns, bAll );
678 * Make a copy of the layout object for a header or footer element from DataTables. Note that
679 * this method will clone the nodes in the layout object.
680 * @returns {Array} Copy of the layout array
681 * @param {Object} aoOriginal Layout array from DataTables (aoHeader or aoFooter)
682 * @param {Object} aiColumns Columns to copy
685 "_fnCopyLayout": function ( aoOriginal, aiColumns )
691 for ( var i=0, iLen=aoOriginal.length ; i<iLen ; i++ )
694 aRow.nTr = $(aoOriginal[i].nTr).clone(true)[0];
696 for ( var j=0, jLen=this.s.iTableColumns ; j<jLen ; j++ )
698 if ( $.inArray( j, aiColumns ) === -1 )
703 var iCloned = $.inArray( aoOriginal[i][j].cell, aCloned );
704 if ( iCloned === -1 )
706 var nClone = $(aoOriginal[i][j].cell).clone(true)[0];
707 aClones.push( nClone );
708 aCloned.push( aoOriginal[i][j].cell );
712 "unique": aoOriginal[i][j].unique
718 "cell": aClones[ iCloned ],
719 "unique": aoOriginal[i][j].unique
724 aReturn.push( aRow );
732 * Clone the DataTable nodes and place them in the DOM (sized correctly)
734 * @param {Object} oClone Object containing the header, footer and body cloned DOM elements
735 * @param {Object} oGrid Grid object containing the display grid elements for the cloned
736 * column (left or right)
737 * @param {Array} aiColumns Column indexes which should be operated on from the DataTable
738 * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)
741 "_fnClone": function ( oClone, oGrid, aiColumns, bAll )
744 i, iLen, j, jLen, jq, nTarget, iColumn, nClone, iIndex;
751 if ( oClone.header !== null )
753 oClone.header.parentNode.removeChild( oClone.header );
755 oClone.header = $(this.dom.header).clone(true)[0];
756 oClone.header.className += " DTFC_Cloned";
757 oClone.header.style.width = "100%";
758 oGrid.head.appendChild( oClone.header );
760 /* Copy the DataTables layout cache for the header for our floating column */
761 var aoCloneLayout = this._fnCopyLayout( this.s.dt.aoHeader, aiColumns );
762 var jqCloneThead = $('>thead', oClone.header);
763 jqCloneThead.empty();
765 /* Add the created cloned TR elements to the table */
766 for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
768 jqCloneThead[0].appendChild( aoCloneLayout[i].nTr );
771 /* Use the handy _fnDrawHead function in DataTables to do the rowspan/colspan
772 * calculations for us
774 this.s.dt.oApi._fnDrawHead( this.s.dt, aoCloneLayout, true );
778 /* To ensure that we copy cell classes exactly, regardless of colspan, multiple rows
779 * etc, we make a copy of the header from the DataTable again, but don't insert the
780 * cloned cells, just copy the classes across. To get the matching layout for the
781 * fixed component, we use the DataTables _fnDetectHeader method, allowing 1:1 mapping
783 var aoCloneLayout = this._fnCopyLayout( this.s.dt.aoHeader, aiColumns );
786 this.s.dt.oApi._fnDetectHeader( aoCurrHeader, $('>thead', oClone.header)[0] );
788 for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
790 for ( j=0, jLen=aoCloneLayout[i].length ; j<jLen ; j++ )
792 aoCurrHeader[i][j].cell.className = aoCloneLayout[i][j].cell.className;
794 // If jQuery UI theming is used we need to copy those elements as well
795 $('span.DataTables_sort_icon', aoCurrHeader[i][j].cell).each( function () {
796 this.className = $('span.DataTables_sort_icon', aoCloneLayout[i][j].cell)[0].className;
801 this._fnEqualiseHeights( 'thead', this.dom.header, oClone.header );
806 if ( this.s.sHeightMatch == 'auto' )
808 /* Remove any heights which have been applied already and let the browser figure it out */
809 $('>tbody>tr', that.dom.body).css('height', 'auto');
812 if ( oClone.body !== null )
814 oClone.body.parentNode.removeChild( oClone.body );
818 oClone.body = $(this.dom.body).clone(true)[0];
819 oClone.body.className += " DTFC_Cloned";
820 oClone.body.style.paddingBottom = this.s.dt.oScroll.iBarWidth+"px";
821 oClone.body.style.marginBottom = (this.s.dt.oScroll.iBarWidth*2)+"px"; /* For IE */
822 if ( oClone.body.getAttribute('id') !== null )
824 oClone.body.removeAttribute('id');
827 $('>thead>tr', oClone.body).empty();
828 $('>tfoot', oClone.body).remove();
830 var nBody = $('tbody', oClone.body)[0];
832 if ( this.s.dt.aiDisplay.length > 0 )
834 /* Copy the DataTables' header elements to force the column width in exactly the
835 * same way that DataTables does it - have the header element, apply the width and
838 var nInnerThead = $('>thead>tr', oClone.body)[0];
839 for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ )
841 iColumn = aiColumns[iIndex];
843 nClone = this.s.dt.aoColumns[iColumn].nTh;
844 nClone.innerHTML = "";
846 oStyle = nClone.style;
847 oStyle.paddingTop = "0";
848 oStyle.paddingBottom = "0";
849 oStyle.borderTopWidth = "0";
850 oStyle.borderBottomWidth = "0";
852 oStyle.width = that.s.aiWidths[iColumn]+"px";
854 nInnerThead.appendChild( nClone );
857 /* Add in the tbody elements, cloning form the master table */
858 $('>tbody>tr', that.dom.body).each( function (z) {
859 var n = this.cloneNode(false);
860 var i = that.s.dt.oFeatures.bServerSide===false ?
861 that.s.dt.aiDisplay[ that.s.dt._iDisplayStart+z ] : z;
862 for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ )
864 iColumn = aiColumns[iIndex];
865 if ( typeof that.s.dt.aoData[i]._anHidden[iColumn] != 'undefined' )
867 nClone = $(that.s.dt.aoData[i]._anHidden[iColumn]).clone(true)[0];
868 n.appendChild( nClone );
871 nBody.appendChild( n );
876 $('>tbody>tr', that.dom.body).each( function (z) {
877 nClone = this.cloneNode(true);
878 nClone.className += ' DTFC_NoData';
879 $('td', nClone).html('');
880 nBody.appendChild( nClone );
884 oClone.body.style.width = "100%";
885 oGrid.body.appendChild( oClone.body );
887 this._fnEqualiseHeights( 'tbody', that.dom.body, oClone.body );
892 if ( this.s.dt.nTFoot !== null )
896 if ( oClone.footer !== null )
898 oClone.footer.parentNode.removeChild( oClone.footer );
900 oClone.footer = $(this.dom.footer).clone(true)[0];
901 oClone.footer.className += " DTFC_Cloned";
902 oClone.footer.style.width = "100%";
903 oGrid.foot.appendChild( oClone.footer );
905 /* Copy the footer just like we do for the header */
906 var aoCloneLayout = this._fnCopyLayout( this.s.dt.aoFooter, aiColumns );
907 var jqCloneTfoot = $('>tfoot', oClone.footer);
908 jqCloneTfoot.empty();
910 for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
912 jqCloneTfoot[0].appendChild( aoCloneLayout[i].nTr );
914 this.s.dt.oApi._fnDrawHead( this.s.dt, aoCloneLayout, true );
918 var aoCloneLayout = this._fnCopyLayout( this.s.dt.aoFooter, aiColumns );
921 this.s.dt.oApi._fnDetectHeader( aoCurrFooter, $('>tfoot', oClone.footer)[0] );
923 for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
925 for ( j=0, jLen=aoCloneLayout[i].length ; j<jLen ; j++ )
927 aoCurrFooter[i][j].cell.className = aoCloneLayout[i][j].cell.className;
931 this._fnEqualiseHeights( 'tfoot', this.dom.footer, oClone.footer );
934 /* Equalise the column widths between the header footer and body - body get's priority */
935 var anUnique = this.s.dt.oApi._fnGetUniqueThs( this.s.dt, $('>thead', oClone.header)[0] );
936 $(anUnique).each( function (i) {
937 iColumn = aiColumns[i];
938 this.style.width = that.s.aiWidths[iColumn]+"px";
941 if ( that.s.dt.nTFoot !== null )
943 anUnique = this.s.dt.oApi._fnGetUniqueThs( this.s.dt, $('>tfoot', oClone.footer)[0] );
944 $(anUnique).each( function (i) {
945 iColumn = aiColumns[i];
946 this.style.width = that.s.aiWidths[iColumn]+"px";
953 * From a given table node (THEAD etc), get a list of TR direct child elements
954 * @param {Node} nIn Table element to search for TR elements (THEAD, TBODY or TFOOT element)
955 * @returns {Array} List of TR elements found
958 "_fnGetTrNodes": function ( nIn )
961 for ( var i=0, iLen=nIn.childNodes.length ; i<iLen ; i++ )
963 if ( nIn.childNodes[i].nodeName.toUpperCase() == "TR" )
965 aOut.push( nIn.childNodes[i] );
973 * Equalise the heights of the rows in a given table node in a cross browser way
975 * @param {String} nodeName Node type - thead, tbody or tfoot
976 * @param {Node} original Original node to take the heights from
977 * @param {Node} clone Copy the heights to
980 "_fnEqualiseHeights": function ( nodeName, original, clone )
982 if ( this.s.sHeightMatch == 'none' && nodeName !== 'thead' && nodeName !== 'tfoot' )
988 i, iLen, iHeight, iHeight2, iHeightOriginal, iHeightClone,
989 rootOriginal = original.getElementsByTagName(nodeName)[0],
990 rootClone = clone.getElementsByTagName(nodeName)[0],
991 jqBoxHack = $('>'+nodeName+'>tr:eq(0)', original).children(':first'),
992 iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height(),
993 anOriginal = this._fnGetTrNodes( rootOriginal ),
994 anClone = this._fnGetTrNodes( rootClone );
996 for ( i=0, iLen=anClone.length ; i<iLen ; i++ )
998 if ( this.s.sHeightMatch == 'semiauto' && typeof anOriginal[i]._DTTC_iHeight != 'undefined' &&
999 anOriginal[i]._DTTC_iHeight !== null )
1001 /* Oddly enough, IE / Chrome seem not to copy the style height - Mozilla and Opera keep it */
1002 if ( $.browser.msie )
1004 $(anClone[i]).children().height( anOriginal[i]._DTTC_iHeight-iBoxHack );
1009 iHeightOriginal = anOriginal[i].offsetHeight;
1010 iHeightClone = anClone[i].offsetHeight;
1011 iHeight = iHeightClone > iHeightOriginal ? iHeightClone : iHeightOriginal;
1013 if ( this.s.sHeightMatch == 'semiauto' )
1015 anOriginal[i]._DTTC_iHeight = iHeight;
1018 /* Can we use some kind of object detection here?! This is very nasty - damn browsers */
1019 if ( $.browser.msie && $.browser.version < 8 )
1021 $(anClone[i]).children().height( iHeight-iBoxHack );
1022 $(anOriginal[i]).children().height( iHeight-iBoxHack );
1026 anClone[i].style.height = iHeight+"px";
1027 anOriginal[i].style.height = iHeight+"px";
1035 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1037 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1041 * FixedColumns default settings for initialisation
1045 FixedColumns.defaults = {
1047 * Number of left hand columns to fix in position
1052 * var oTable = $('#example').dataTable( {
1053 * "sScrollX": "100%"
1055 * new FixedColumns( oTable, {
1062 * Number of right hand columns to fix in position
1067 * var oTable = $('#example').dataTable( {
1068 * "sScrollX": "100%"
1070 * new FixedColumns( oTable, {
1071 * "iRightColumns": 1
1077 * Draw callback function which is called when FixedColumns has redrawn the fixed assets
1078 * @type function(object, object):void
1082 * var oTable = $('#example').dataTable( {
1083 * "sScrollX": "100%"
1085 * new FixedColumns( oTable, {
1086 * "fnDrawCallback": function () {
1087 * alert( "FixedColumns redraw" );
1091 "fnDrawCallback": null,
1094 * Type of left column size calculation. Can take the values of "fixed", whereby the iLeftWidth
1095 * value will be treated as a pixel value, or "relative" for which case iLeftWidth will be
1096 * treated as a percentage value.
1101 * var oTable = $('#example').dataTable( {
1102 * "sScrollX": "100%"
1104 * new FixedColumns( oTable, {
1105 * "sLeftWidth": "relative",
1106 * "iLeftWidth": 10 // percentage
1109 "sLeftWidth": "fixed",
1112 * Width to set for the width of the left fixed column(s) - note that the behaviour of this
1113 * property is directly effected by the sLeftWidth property. If not defined then this property
1114 * is calculated automatically from what has been assigned by DataTables.
1119 * var oTable = $('#example').dataTable( {
1120 * "sScrollX": "100%"
1122 * new FixedColumns( oTable, {
1123 * "iLeftWidth": 100 // pixels
1129 * Type of right column size calculation. Can take the values of "fixed", whereby the
1130 * iRightWidth value will be treated as a pixel value, or "relative" for which case
1131 * iRightWidth will be treated as a percentage value.
1136 * var oTable = $('#example').dataTable( {
1137 * "sScrollX": "100%"
1139 * new FixedColumns( oTable, {
1140 * "sRightWidth": "relative",
1141 * "iRightWidth": 10 // percentage
1144 "sRightWidth": "fixed",
1147 * Width to set for the width of the right fixed column(s) - note that the behaviour of this
1148 * property is directly effected by the sRightWidth property. If not defined then this property
1149 * is calculated automatically from what has been assigned by DataTables.
1154 * var oTable = $('#example').dataTable( {
1155 * "sScrollX": "100%"
1157 * new FixedColumns( oTable, {
1158 * "iRightWidth": 200 // pixels
1161 "iRightWidth": null,
1164 * Height matching algorthim to use. This can be "none" which will result in no height
1165 * matching being applied by FixedColumns (height matching could be forced by CSS in this
1166 * case), "semiauto" whereby the height calculation will be performed once, and the result
1167 * cached to be used again (fnRecalculateHeight can be used to force recalculation), or
1168 * "auto" when height matching is performed on every draw (slowest but must accurate)
1173 * var oTable = $('#example').dataTable( {
1174 * "sScrollX": "100%"
1176 * new FixedColumns( oTable, {
1177 * "sHeightMatch": "auto"
1180 "sHeightMatch": "semiauto"
1186 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1188 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1192 * Name of this class
1195 * @default FixedColumns
1197 FixedColumns.prototype.CLASS = "FixedColumns";
1201 * FixedColumns version
1202 * @constant FixedColumns.VERSION
1207 FixedColumns.VERSION = "2.0.3";
1211 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1212 * Fired events (for documentation)
1213 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1217 * Event fired whenever FixedColumns redraws the fixed columns (i.e. clones the table elements from the main DataTable). This will occur whenever the DataTable that the FixedColumns instance is attached does its own draw.
1218 * @name FixedColumns#draw
1220 * @param {event} e jQuery event object
1221 * @param {object} o Event parameters from FixedColumns
1222 * @param {object} o.leftClone Instance's object dom.clone.left for easy reference. This object contains references to the left fixed clumn column's nodes
1223 * @param {object} o.rightClone Instance's object dom.clone.right for easy reference. This object contains references to the right fixed clumn column's nodes
1226 })(jQuery, window, document);