Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this jquery so slow?

I'm using a jquery plugin which fixes the headers on a html table that I generate. Unfortunately the performance of the plugin is very slow and I've narrowed it down to the following code:

var $tbl = $(this);  
var $tblhfixed = $tbl.find("thead");  
$tblhfixed.find("th").each(function ()    
    $(this).css("width", $(this).width());  
});

This is taking about 40 seconds on a table with 2,000 rows in ie. Does anyone know why it's so slow and more importantly how can I make this faster? I've tried many other plugins and this is the only one which works how I want it to. Thanks for any help

like image 693
nzyme Avatar asked Mar 03 '11 11:03

nzyme


3 Answers

I guess you faced with the same problem that I had some time ago. It called a "Recalculate layout" or something.

Try to separate this script onto two loops, like this:

var $tbl = $(this);
var $tblhfixed = $tbl.find("thead");
var widths = [];

// 1.
$tblhfixed.find("th").each(function ()
    widths.push($(this).width());
});

// 2.
$tblhfixed.find("th").each(function (index, element)
    $(this).css("width", widths[index]);
});

First one will calculate all the widths. Second one will apply them to TH's

UPD: You may increase performance by placing this code between 1. and 2.:

$tblhfixed.hide();

and show it again after 2.:

$tblhfixed.show();
like image 116
Genius Avatar answered Oct 23 '22 04:10

Genius


the culprit is probably the .each.

The reason is that when you iterate using .eachinstead of a normal loop, you call a function for each iteration. a function call has a pretty big overhead in this case, since a new callstack has to be created for each iteration.

To make it faster change

$tblhfixed.find("th").each(function ()    
    $(this).css("width", $(this).width());  
});

to

var elms = $tblhfixed.find("th");
for(var i=0, elm;elm = elms[i];i++) {
    elm.css("width", elm.width());  
}
like image 44
Martin Jespersen Avatar answered Oct 23 '22 03:10

Martin Jespersen


First, you should use find() only when you need to pass through all nested nodes. Right here you can use children().

Second, each time $(this) creates new instance of jQuery object, while you can create it once:

var $this = $(this);

Each time $(this).width() is recalculated. Make sure that you need it to be recalculated. Or do:

var tableWidth = $this.width();

And third, according to @Martin Jespersen, each iteration the function object is created.

Also you don't need jQuery here at all. You can access DOM directly:

var tableWidth = ...; // get the table width the way you want
var thead = this.tHead;
var thRow = thead.rows[0];
for (var i = 0; i < thRow.length; ++i) {
  thRow.cells[i].style.width = tableWidth + "px";
}
like image 34
artyom.stv Avatar answered Oct 23 '22 04:10

artyom.stv