1
0
mirror of https://github.com/DataTables/DataTables.git synced 2025-01-30 23:52:11 +01:00

Size - refactor sizing functions to reduce file size and modernise code

- The _fnCalculateColumnWidths is probably the oldest function in
  DataTables which has mostly reamined as it was 5 years ago! However,
  its time to trim it down a bit and optimise now. This rewrite has all
  the same actions, but results in a minified file which is about 700
  bytes smaller than before.
This commit is contained in:
Allan Jardine 2013-10-11 14:21:38 +01:00
parent 920b7671fb
commit 5c0cdd86cd
2 changed files with 200 additions and 293 deletions

View File

@ -1 +1 @@
4daf47de0baf63177f503990d32aa32614968531 a0417585115ca4bb8a6675eed7738724432ad032

View File

@ -3414,34 +3414,9 @@
} }
} }
/**
* Convert a CSS unit width to pixels (e.g. 2em)
* @param {string} sWidth width to be converted
* @param {node} nParent parent to get the with for (required for relative widths) - optional
* @returns {int} iWidth width in pixels
* @memberof DataTable#oApi
*/
function _fnConvertToWidth ( sWidth, nParent )
{
if ( ! sWidth )
{
return 0;
}
if ( ! nParent )
{
nParent = document.body;
}
var n = $('<div/>') var __re_html_remove = /<.*?>/g;
.css( 'width', _fnStringToCss( sWidth ) )
.appendTo( nParent );
var width = n[0].offsetWidth;
n.remove();
return width;
}
/** /**
@ -3451,235 +3426,159 @@
*/ */
function _fnCalculateColumnWidths ( oSettings ) function _fnCalculateColumnWidths ( oSettings )
{ {
var table = oSettings.nTable; var
var columns = oSettings.aoColumns; table = oSettings.nTable,
var column; columns = oSettings.aoColumns,
var iTableWidth = table.offsetWidth; scroll = oSettings.oScroll,
var iUserInputs = 0; scrollY = scroll.sY,
var iTmpWidth; scrollX = scroll.sX,
var iVisibleColumns = 0; scrollXInner = scroll.sXInner,
var iColums = columns.length; columnCount = columns.length,
var i, iIndex, iCorrector, iWidth; visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
var oHeaders = $('th', oSettings.nTHead); headerCells = $('th', oSettings.nTHead),
var widthAttr = table.getAttribute('width'); tableWidthAttr = table.getAttribute('width'),
var nWrapper = table.parentNode; tableContainer = table.parentNode,
userInputs = false,
i, column, columnIdx, width, outerWidth;
/* Convert any user input sizes into pixel sizes */ /* Convert any user input sizes into pixel sizes */
for ( i=0 ; i<iColums ; i++ ) for ( i=0 ; i<visibleColumns.length ; i++ ) {
{ column = columns[ visibleColumns[i] ];
if ( columns[i].bVisible )
{
iVisibleColumns++;
if ( columns[i].sWidth !== null ) if ( column.sWidth !== null ) {
{ column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
iTmpWidth = _fnConvertToWidth( columns[i].sWidthOrig,
nWrapper );
if ( iTmpWidth !== null )
{
columns[i].sWidth = _fnStringToCss( iTmpWidth );
}
iUserInputs++; userInputs = true;
}
} }
} }
/* If the number of columns in the DOM equals the number that we have to process in /* If the number of columns in the DOM equals the number that we have to
* DataTables, then we can use the offsets that are created by the web-browser. No custom * process in DataTables, then we can use the offsets that are created by
* sizes can be set in order for this to happen, nor scrolling used * the web- browser. No custom sizes can be set in order for this to happen,
* nor scrolling used
*/ */
if ( iColums == oHeaders.length && iUserInputs === 0 && iVisibleColumns == iColums && if ( ! userInputs && ! scrollX && ! scrollY &&
oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" ) columnCount == _fnVisbleColumns( oSettings ) &&
{ columnCount == headerCells.length
for ( i=0 ; i<columns.length ; i++ ) ) {
{ for ( i=0 ; i<columnCount ; i++ ) {
iTmpWidth = $(oHeaders[i]).width(); columns[i].sWidth = _fnStringToCss( headerCells.eq(i).width() );
if ( iTmpWidth !== null )
{
columns[i].sWidth = _fnStringToCss( iTmpWidth );
}
} }
} }
else else
{ {
/* Otherwise we are going to have to do some calculations to get the width of each column. // Otherwise construct a single row table with the widest node in the
* Construct a 1 row table with the widest node in the data, and any user defined widths, // data, assign any user defined widths, then insert it into the DOM and
* then insert it into the DOM and allow the browser to do all the hard work of // allow the browser to do all the hard work of calculating table widths
* calculating table widths. var tmpTable = $( table.cloneNode( false ) )
*/ .css( 'visibility', 'hidden' )
var .removeAttr( 'id' )
nCalcTmp = table.cloneNode( false ), .append( $(oSettings.nTHead).clone( false ) )
nTheadClone = oSettings.nTHead.cloneNode(true), .append( $(oSettings.nTFoot).clone( false ) )
nBody = document.createElement( 'tbody' ), .append( $('<tbody><tr/></tbody>') );
nTr = document.createElement( 'tr' ),
nDivSizing;
nCalcTmp.removeAttribute( "id" ); // Remove any assigned widths from the footer (from scrolling)
nCalcTmp.appendChild( nTheadClone ); tmpTable.find('tfoot th, tfoot td').css('width', '');
if ( oSettings.nTFoot !== null )
{ var tr = tmpTable.find( 'tbody tr' );
nCalcTmp.appendChild( oSettings.nTFoot.cloneNode(true) );
_fnApplyToChildren( function(n) { // Apply custom sizing to the cloned header
n.style.width = ""; headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
}, nCalcTmp.getElementsByTagName('tr') );
for ( i=0 ; i<visibleColumns.length ; i++ ) {
column = columns[ visibleColumns[i] ];
headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
_fnStringToCss( column.sWidthOrig ) :
'';
} }
nCalcTmp.appendChild( nBody ); // Find the widest cell for each column and put it into the table
nBody.appendChild( nTr ); if ( oSettings.aoData.length ) {
for ( i=0 ; i<visibleColumns.length ; i++ ) {
columnIdx = visibleColumns[i];
column = columns[ columnIdx ];
/* Remove any sizing that was previously applied by the styles */ $( _fnGetWidestNode( oSettings, columnIdx ) )
var jqColSizing = $('thead th', nCalcTmp); .clone( false )
if ( jqColSizing.length === 0 ) .append( column.sContentPadding )
{ .appendTo( tr );
jqColSizing = $('tbody tr:eq(0)>td', nCalcTmp);
}
/* Apply custom sizing to the cloned header */
var nThs = _fnGetUniqueThs( oSettings, nTheadClone );
iCorrector = 0;
for ( i=0 ; i<iColums ; i++ )
{
column = columns[i];
if ( column.bVisible && column.sWidthOrig !== null && column.sWidthOrig !== "" )
{
nThs[i-iCorrector].style.width = _fnStringToCss( column.sWidthOrig );
}
else if ( column.bVisible )
{
nThs[i-iCorrector].style.width = "";
}
else
{
iCorrector++;
} }
} }
/* Find the biggest td for each column and put it into the table */ // Table has been built, attach to the document so we can work with it
for ( i=0 ; i<iColums ; i++ ) tmpTable.appendTo( tableContainer );
{
column = columns[i];
if ( column.bVisible ) // When scrolling (X or Y) we want to set the width of the table as
{ // appropriate. However, when not scrolling leave the table width as it
var nTd = _fnGetWidestNode( oSettings, i ); // is. This results in slightly different, but I think correct behaviour
if ( nTd !== null ) if ( scrollX && scrollXInner ) {
{ tmpTable.width( scrollXInner );
nTd = nTd.cloneNode(true);
if ( column.sContentPadding !== "" )
{
nTd.innerHTML += column.sContentPadding;
} }
nTr.appendChild( nTd ); else if ( scrollX ) {
tmpTable.css( 'width', 'auto' );
if ( tmpTable.width() < tableContainer.offsetWidth ) {
tmpTable.width( tableContainer.offsetWidth );
} }
} }
else if ( scrollY ) {
tmpTable.width( tableContainer.offsetWidth );
}
else if ( tableWidthAttr ) {
tmpTable.width( tableWidthAttr );
}
// Take into account the y scrollbar
_fnScrollingWidthAdjust( oSettings, tmpTable[0] );
// Browsers need a bit of a hand when a width is assigned to any columns
// when x-scrolling as they tend to collapse the table to the min-width,
// even if we sent the column widths. So we need to keep track of what
// the table width should be by summing the user given values, and the
// automatic values
if ( scrollX )
{
var total = 0;
for ( i=0 ; i<visibleColumns.length ; i++ ) {
column = columns[ visibleColumns[i] ];
outerWidth = $(headerCells[i]).outerWidth();
total += column.sWidthOrig === null ?
outerWidth :
parseInt( column.sWidth, 10 ) + outerWidth - $(headerCells[i]).width();
}
tmpTable.width( _fnStringToCss( total ) );
table.style.width = _fnStringToCss( total );
}
// Get the width of each column in the constructed table
for ( i=0 ; i<visibleColumns.length ; i++ ) {
column = columns[ visibleColumns[i] ];
width = $(headerCells[i]).width();
if ( width ) {
column.sWidth = _fnStringToCss( width );
}
} }
/* Build the table and 'display' it */ table.style.width = _fnStringToCss( tmpTable.css('width') );
nWrapper.appendChild( nCalcTmp );
/* When scrolling (X or Y) we want to set the width of the table as appropriate. However, // Finished with the table - ditch it
* when not scrolling leave the table width as it is. This results in slightly different, tmpTable.remove();
* but I think correct behaviour
*/
if ( oSettings.oScroll.sX !== "" && oSettings.oScroll.sXInner !== "" )
{
nCalcTmp.style.width = _fnStringToCss(oSettings.oScroll.sXInner);
}
else if ( oSettings.oScroll.sX !== "" )
{
nCalcTmp.style.width = "";
if ( $(nCalcTmp).width() < nWrapper.offsetWidth )
{
nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
}
}
else if ( oSettings.oScroll.sY !== "" )
{
nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
}
else if ( widthAttr )
{
nCalcTmp.style.width = _fnStringToCss( widthAttr );
}
nCalcTmp.style.visibility = "hidden";
/* Scrolling considerations */
_fnScrollingWidthAdjust( oSettings, nCalcTmp );
/* Read the width's calculated by the browser and store them for use by the caller. We
* first of all try to use the elements in the body, but it is possible that there are
* no elements there, under which circumstances we use the header elements
*/
var oNodes = $("tbody tr:eq(0)", nCalcTmp).children();
if ( oNodes.length === 0 )
{
oNodes = _fnGetUniqueThs( oSettings, $('thead', nCalcTmp)[0] );
} }
/* Browsers need a bit of a hand when a width is assigned to any columns when // If there is a width attr, we want to attach an event listener which
* x-scrolling as they tend to collapse the table to the min-width, even if // allows the table sizing to automatically adjust when the window is
* we sent the column widths. So we need to keep track of what the table width // resized. Use the width attr rather than CSS, since we can't know if the
* should be by summing the user given values, and the automatic values // CSS is a relative value or absolute - DOM read is always px.
*/ if ( tableWidthAttr ) {
if ( oSettings.oScroll.sX !== "" ) table.style.width = _fnStringToCss( tableWidthAttr );
{
var iTotal = 0;
iCorrector = 0;
for ( i=0 ; i<columns.length ; i++ )
{
column = columns[i];
if ( column.bVisible ) if ( ! oSettings._reszEvt ) {
{ $(window).bind('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
if ( column.sWidthOrig === null )
{
iTotal += $(oNodes[iCorrector]).outerWidth();
}
else
{
iTotal += parseInt(column.sWidth.replace('px',''), 10) +
($(oNodes[iCorrector]).outerWidth() - $(oNodes[iCorrector]).width());
}
iCorrector++;
}
}
nCalcTmp.style.width = _fnStringToCss( iTotal );
table.style.width = _fnStringToCss( iTotal );
}
iCorrector = 0;
for ( i=0 ; i<columns.length ; i++ )
{
column = columns[i];
if ( column.bVisible )
{
iWidth = $(oNodes[iCorrector]).width();
if ( iWidth !== null && iWidth > 0 )
{
column.sWidth = _fnStringToCss( iWidth );
}
iCorrector++;
}
}
var cssWidth = $(nCalcTmp).css('width');
table.style.width = (cssWidth.indexOf('%') !== -1) ?
cssWidth : _fnStringToCss( $(nCalcTmp).outerWidth() );
nCalcTmp.parentNode.removeChild( nCalcTmp );
}
if ( widthAttr )
{
table.style.width = _fnStringToCss( widthAttr );
if ( ! oSettings._reszEvt )
{
$(window).bind('resize.DT-'+oSettings.sInstance, throttle( function () {
_fnAdjustColumnSizing( oSettings ); _fnAdjustColumnSizing( oSettings );
} ) ); } ) );
@ -3689,8 +3588,7 @@
} }
// @todo Move into a private functions file or make a proper DT function of it function _fnThrottle( fn ) {
function throttle( fn ) {
var var
frequency = 200, frequency = 200,
last, last,
@ -3718,110 +3616,118 @@
/** /**
* Adjust a table's width to take account of scrolling * Convert a CSS unit width to pixels (e.g. 2em)
* @param {string} width width to be converted
* @param {node} parent parent to get the with for (required for relative widths) - optional
* @returns {int} width in pixels
* @memberof DataTable#oApi
*/
function _fnConvertToWidth ( width, parent )
{
if ( ! width ) {
return 0;
}
var n = $('<div/>')
.css( 'width', _fnStringToCss( width ) )
.appendTo( parent || document.body );
var val = n[0].offsetWidth;
n.remove();
return val;
}
/**
* Adjust a table's width to take account of vertical scroll bar
* @param {object} oSettings dataTables settings object * @param {object} oSettings dataTables settings object
* @param {node} n table node * @param {node} n table node
* @memberof DataTable#oApi * @memberof DataTable#oApi
*/ */
function _fnScrollingWidthAdjust ( oSettings, n )
function _fnScrollingWidthAdjust ( settings, n )
{ {
if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY !== "" ) var scroll = settings.oScroll;
{
/* When y-scrolling only, we want to remove the width of the scroll bar so the table if ( scroll.sX || scroll.sY ) {
* + scroll bar will fit into the area avaialble. // When y-scrolling only, we want to remove the width of the scroll bar
*/ // so the table + scroll bar will fit into the area available, otherwise
var iOrigWidth = $(n).width(); // we fix the table at its current size with no adjustment
n.style.width = _fnStringToCss( $(n).outerWidth()-oSettings.oScroll.iBarWidth ); var correction = ! scroll.sX ? scroll.iBarWidth : 0;
} n.style.width = _fnStringToCss( $(n).outerWidth() - correction );
else if ( oSettings.oScroll.sX !== "" )
{
/* When x-scrolling both ways, fix the table at it's current size, without adjusting */
n.style.width = _fnStringToCss( $(n).outerWidth() );
} }
} }
/** /**
* Get the widest node * Get the widest node
* @param {object} oSettings dataTables settings object * @param {object} settings dataTables settings object
* @param {int} iCol column of interest * @param {int} colIdx column of interest
* @returns {node} widest table node * @returns {node} widest table node
* @memberof DataTable#oApi * @memberof DataTable#oApi
*/ */
function _fnGetWidestNode( oSettings, iCol ) function _fnGetWidestNode( settings, colIdx )
{
var iMaxIndex = _fnGetMaxLenString( oSettings, iCol );
if ( iMaxIndex < 0 )
{ {
var idx = _fnGetMaxLenString( settings, colIdx );
if ( idx < 0 ) {
return null; return null;
} }
var data = oSettings.aoData[ iMaxIndex ]; var data = settings.aoData[ idx ];
return data.nTr === null ? // Might not have been created when deferred rendering return ! data.nTr ? // Might not have been created when deferred rendering
$('<td/>').html( _fnGetCellData( oSettings, iMaxIndex, iCol, 'display' ) )[0] : $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
data.anCells[ iCol ]; data.anCells[ colIdx ];
} }
var __re_html_remove = /<.*?>/g;
/** /**
* Get the maximum strlen for each data column * Get the maximum strlen for each data column
* @param {object} oSettings dataTables settings object * @param {object} settings dataTables settings object
* @param {int} iCol column of interest * @param {int} colIdx column of interest
* @returns {string} max string length for each column * @returns {string} max string length for each column
* @memberof DataTable#oApi * @memberof DataTable#oApi
*/ */
function _fnGetMaxLenString( oSettings, iCol ) function _fnGetMaxLenString( settings, colIdx )
{ {
var s, iMax=-1, iMaxIndex = -1; var s, max=-1, maxIdx = -1;
for ( var i=0 ; i<oSettings.aoData.length ; i++ ) for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
{ s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
s = _fnGetCellData( oSettings, i, iCol, 'display' )+'';
s = s.replace( __re_html_remove, '' ); s = s.replace( __re_html_remove, '' );
if ( s.length > iMax ) if ( s.length > max ) {
{ max = s.length;
iMax = s.length; maxIdx = i;
iMaxIndex = i;
} }
} }
return iMaxIndex; return maxIdx;
} }
/** /**
* Append a CSS unit (only if required) to a string * Append a CSS unit (only if required) to a string
* @param {array} aArray1 first array * @param {string} value to css-ify
* @param {array} aArray2 second array * @returns {string} value with css unit
* @returns {int} 0 if match, 1 if length is different, 2 if no match
* @memberof DataTable#oApi * @memberof DataTable#oApi
*/ */
function _fnStringToCss( s ) function _fnStringToCss( s )
{ {
if ( s === null ) if ( s === null ) {
{ return '0px';
return "0px";
} }
if ( typeof s == 'number' ) if ( typeof s == 'number' ) {
{ return s < 0 ?
if ( s < 0 ) '0px' :
{ s+'px';
return "0px";
}
return s+"px";
} }
/* Check if the last character is not 0-9 */ // Check it has a unit character already
var c = s.charCodeAt( s.length-1 ); return s.match(/\d$/) ?
if (c < 0x30 || c > 0x39) s+'px' :
{ s;
return s;
}
return s+"px";
} }
@ -3832,8 +3738,9 @@
*/ */
function _fnScrollBarWidth () function _fnScrollBarWidth ()
{ {
if ( ! DataTable.__scrollbarWidth ) // On first run a static variable is set, since this is only needed once.
{ // Subsequent runs will just use the previously calculated value
if ( ! DataTable.__scrollbarWidth ) {
var inner = $('<p/>').css( { var inner = $('<p/>').css( {
width: '100%', width: '100%',
height: 200, height: 200,
@ -3857,8 +3764,8 @@
var w1 = inner.offsetWidth; var w1 = inner.offsetWidth;
outer.css( 'overflow', 'scroll' ); outer.css( 'overflow', 'scroll' );
var w2 = inner.offsetWidth; var w2 = inner.offsetWidth;
if ( w1 === w2 )
{ if ( w1 === w2 ) {
w2 = outer[0].clientWidth; w2 = outer[0].clientWidth;
} }