nzyme
nzyme

Reputation: 185

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

Upvotes: 6

Views: 2532

Answers (5)

nerdess
nerdess

Reputation: 10930

it appears that $.width() is 99 times slower than the native get(0).clientWidth, check out this test: http://jsperf.com/jq-width-vs-client-width

Upvotes: 1

Chii
Chii

Reputation: 14746

you should not repeat $(this) inside your function passed into .each(). wrapping an element has non-trivial overhead, which is not ok when you have 20k elements. you want to eliminate as much work as possible inside the .each() call, or eliminate it altogether.

Also, why query find() twice, when you can do this instead, which should give you the same results:

$ths = $('table thead th'); //or tableid, or something
$ths.css('width', $ths.width());

Upvotes: 1

Genius
Genius

Reputation: 1782

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();

Upvotes: 2

artyom.stv
artyom.stv

Reputation: 2164

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";
}

Upvotes: 1

Martin Jespersen
Martin Jespersen

Reputation: 26183

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());  
}

Upvotes: 1

Related Questions