PATCH: Small performance improvement

PATCH: Small performance improvement

jlabancajlabanca Posts: 6Questions: 0Answers: 0
edited November 2012 in General
_fnConvertToWidth() creates a div element to convert the CSS width to pixels, then removes the div. This invalidates layout, forces layout calculations, then invalidates it again because we remove the div (takes about 30ms in my case). And it happens for every column when autoWidth is enabled! As an optimization, we can measure all of the widths at once so we only force one browser layout. And as another optimization, we don't need to touch the DOM if the sWidth is specified in pixels anyway (ex 100px).

The following rewrite of _fnConvertToWidth can take an array of widths to convert, and converts them in one pass. It also checks if the width is specified in pixels, and justs uses the value without touching the DOM. That feature would probably improve the performance of bAutoWidth, but since I'm not using autoWidth, I'll leave that for you :)

If you don't need the whole thing, I recommend at least taking the regex portion that checks to see if the width is specified in px. That's a simple fix that increases performance dramatically.

[code]
/**
* Convert a CSS unit width to pixels (e.g. 2em). If sWidth is an array,
* every width in the array is converted to pixels and an array of widths
* with corresponding indexes is returned.
*
* @param {string||array} sWidth width (or array of widths) to be converted
* @param {node} nParent parent to get the with for (required for relative widths) - optional
* @returns {int||array} iWidth width (or an array of widths) in pixels
* @memberof DataTable#oApi
*/
function _fnConvertToWidth ( sWidth, nParent )
{
if ( !sWidth || sWidth === null || sWidth === '' ) {
return 0;
}

if ( !nParent )
{
nParent = document.body;
}

// Convert sWidth to an array.
var isArray = $.isArray(sWidth);
if (!isArray)
{
sWidth = [ sWidth ];
}

/*
* Attach a div for each sWidth, which invalidates layout. We attach all
* of the divs at once to avoid browser layout.
*/
var nTmps = [];
var iWidths = [];
for (var i = 0; i < sWidth.length; i++) {
var curWidth = sWidth[i];
if ( !curWidth || curWidth === null || curWidth === '' ) {
// Invalid width, assume 0.
nTmps[i] = null;
iWidths[i] = 0;
} else {
var match = $.trim(sWidth[i]).match(/^([0-9]+)px$/i);
if (match) {
// No need to measure, we'll just use the pixel value.
nTmps[i] = null;
iWidths[i] = parseInt(match[1]);
} else {
// Create a div to measure the width.
nTmps[i] = document.createElement( "div" );
nTmps[i].style.width = _fnStringToCss( sWidth[i] );
nParent.appendChild( nTmps[i] );
}
}
}

/*
* Measure each div. This forces browser layout.
*/
for (var i = 0; i < sWidth.length; i++) {
if (nTmps[i]) {
iWidths[i] = nTmps[i].offsetWidth;
}
}

/*
* Remove all divs. Invalidates browser layout.
*/
for (var i = 0; i < sWidth.length; i++) {
if (nTmps[i]) {
nParent.removeChild( nTmps[i] );
}
}

/*
* If the user passed in an array, return an array of widths. If
* the user passed a single value, return a single width.
*/
return ( isArray ? iWidths : iWidths[0] );
}
[/code]

I'm in the process of trying to optimize our datatable for text based column filtering, with the goal of being able to filter as fast as the user types in IE7 (crazy, I know). Currently, even with the Scroller plugin, filtering a large table takes about 300ms. Not bad, but there is still a lag as the user types each key.

Replies

  • allanallan Posts: 63,532Questions: 1Answers: 10,475 Site admin
    Sounds like a very good move. Thanks for this! I'll look at integrating it into DataTables 1.10.

    Allan
This discussion has been closed.