6

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

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
nzyme
  • 185
  • 1
  • 9
  • Does the number of rows make a difference (Have you tested this)? You're only manipulating the table header. How many columns are there? You should use a filter rather than a find. http://groups.google.com/group/jquery-en/browse_thread/thread/533451087251c952/9bb31c108c089c4f and http://www.learningjquery.com/2006/12/how-to-get-anything-you-want-part-2 – Shiv Kumar Mar 03 '11 at 11:10
  • 1
    What is `this`? What is the context? A http://jsfiddle.net would be great :) – Felix Kling Mar 03 '11 at 11:10
  • Sorry, 'this' is a reference to the table I'm applying the plugin to – nzyme Mar 03 '11 at 11:14
  • You could try: `$tblhfixed.find("th").width(function(i, val) { return val;});` – Felix Kling Mar 03 '11 at 11:18
  • 1
    Did you ever find a solution? I'm chasing almost exactly the same problem and am suspecting jQuery's width() method which appears to be taking ~35 seconds for <6,000 elements. – Tom Morris Sep 12 '12 at 17:49
  • @TomMorris did you find a solution? chasing the same problem :P – thedayturns Feb 17 '13 at 02:20
  • @thedayturns No, I ran out of time to investigate, but you can read more about my version of the problem in my question http://stackoverflow.com/questions/12414150/jquery-width-method-performance – Tom Morris Feb 17 '13 at 22:00
  • Take the table out of the document and add it to a document fragment > do all processing on table > add table back into document. Result > Layout and styling is only painted once by the browser. – Ally Dec 20 '13 at 15:02

5 Answers5

2

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();
Genius
  • 1,784
  • 14
  • 12
  • That really helped me, although I do not fully understand why and really my code still works rather slow - definitely faster than before, but still slow. – Graf Sep 30 '13 at 13:10
1

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

nerdess
  • 10,051
  • 10
  • 45
  • 55
1

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());  
}
Martin Jespersen
  • 25,743
  • 8
  • 56
  • 68
1

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());
Chii
  • 14,540
  • 3
  • 37
  • 44
1

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";
}
artyom.stv
  • 2,097
  • 1
  • 24
  • 42