Erik Dekker
Erik Dekker

Reputation: 2433

How can I optimize execution time of this jquery loop?

I have a loop thats going through all div's in a table cel with class 'GridCell'. It is required that this happens in some situations, when the grid or columns are resized.

I extended the number of rows and columns, to see more time differences, and the loop is around 750 ms right now.

But what I don't understand, is that 'just a part of the loop' is a lot faster. See the following three loops. The first is slow. The second and third loop that only do a part of the first loop are really fast.

//Around 750 ms
$('table.Grid tbody td.GridCell > div').each(function() {
    var width = $(this).parent().width();
    $(this).css('width', width - 3);
});

//Around 60 ms
$('table.Grid tbody td.GridCell > div').each(function() {
    var width = $(this).parent().width();
});

//Around 15 ms
$('table.Grid tbody td.GridCell > div').each(function() {
    $(this).css('width', 100);
});

So one line, is only 60 or 15 ms, but the two together are 750. What makes this difference?

p.s. It doesn't matter in what sequence I execute the loops. The first loop is always a lot slower then the others, also when that loop is executed last.

Upvotes: 4

Views: 699

Answers (9)

GajendraSinghParihar
GajendraSinghParihar

Reputation: 9131

what if you use

$('table.Grid tbody td.GridCell > div').each(function(i,domEle) {
    $(domEle).css('width', $(domEle).parent().width() - 3);
});

And also try to use optimize selector in jQuery

Upvotes: 0

Yoshi
Yoshi

Reputation: 54649

// collect the widths from the first row only
var widths = $('table.Grid tbody tr:first-child td.GridCell').map(function(idx) {
  return $(this).width() - 3;

  // or use:
  // return this.clientWidth - 3;
  // if you're not targeting IE <= 7
});

// apply the widths to each div
$('table.Grid tbody td.GridCell > div').each(function(idx) {
  this.style.width = widths[idx % widths.length] + 'px';
});

Upvotes: 2

Alnitak
Alnitak

Reputation: 339816

OK, here's another alternative:

$('table.Grid tbody td.GridCell > div').css('width', function() {
    return $(this.parentNode).width() - 3;
});

Upvotes: 0

Alnitak
Alnitak

Reputation: 339816

var $rows = $('table.Grid tbody').children('tr');

// we only need the widths from the first row
var widths = $rows.first().children('td').map(function() {
    return $(this).width() - 3;
}).get();

// process each row individually
$rows.each(function() {
    $('td.gridCell > div', this).css('width', function(i) {
         return widths[i];
    });
});

Upvotes: 1

Oleg
Oleg

Reputation: 24988

This does not have to be a JS issue! Forget the loops, you don't need to use JS/jQuery to achieve the desired functionality at all.

To make the child div of .GridCell take up all available width minus 3px, you can use the following CSS rule:

.GridCell>div {
  width:auto;             /*take up all available width (for block elements)*/
  margin:0 1.5px;         /*add a 1.5px buffer on both sides of the div*/
}

If you want to be really fancy and don't care about somewhat poor browser compatibility matrix, you could rely on CSS3 calc():

.GridCell>div {
  width:calc(100% - 3px); /*look up vendor-specific prefixes of the property
                            if you want for this to work on more browsers*/
  margin: 0 auto;         /*now that the width is calculated correctly above,
                            center the div. Or don't!*/
}

Et voila

Upvotes: 0

Viktor S.
Viktor S.

Reputation: 12815

Hard for me to say exact details, but I understand this problem like this:

var width = $(this).parent().width(); - you call this and browser is forced to get you real dimentions of selected element. $(this).css('width', 100); - this cause browser to reflow the document. So you run this line and browser must recalculate a part of page.

Why first works slower than two other loops? I see it like when you do only var width = $(this).parent().width(); it can calculate all width once and give them from cache. Now when you do only this line $(this).css('width', 100); - browser can wait untill script is executed. And redraw all update elements at once. But when you do the first loop you force browser calculate width, than you update some element. Dimentions cache is broken. Now you again want to get a width and browser must redraw page. So each cycle it redraws a page in opposit to other two loops when it can do that only once.

Upvotes: 0

David Hellsing
David Hellsing

Reputation: 108500

In the first loop, you are calculating a computed width, then applying that to another element for each iteration. In the second loop, all you do is computing the width, and assigning it to a variable. And in the third loop, you are simply applying static inline styles.

What I’m saying is that the last two loops combined does not equal the functionality of the first, so it’s no surprise that the first one slower than the other two combined.

You should try something like:

var $divs = $('table.Grid tbody td.GridCell > div'),
    m = [];

// case 1
$divs.each(function() {
    var width = $(this).parent().width();
    $(this).css('width', width - 3);
});

// case 2
$divs.each(function() {
    m.push( $(this).parent().width() );
}).each(function(i) {
    $(this).css('width', m[i] - 3);
});

I made a simple perf test here: http://jsperf.com/tablewidth and the difference seems to be very small.

Upvotes: 2

Fabrizio Calderan
Fabrizio Calderan

Reputation: 123377

in your first example

$('table.Grid tbody td.GridCell > div').each(function() {
    var width = $(this).parent().width();
    $(this).css('width', width - 3);
});

you're evaluating $(this) twice. try to write instead

$('table.Grid tbody td.GridCell > div').each(function() {
    var $this = $(this);
    var width = $this.parent().width();
    $this.css('width', width - 3);
});

or even better (edit: not sure) try also to use the current element passed along the each() method

$('table.Grid tbody td.GridCell > div').each(function(i, el) {
    var $this = $(el);
    var width = $this.parent().width();
    $this.css('width', width - 3);
});

Upvotes: 0

Anthony Palmer
Anthony Palmer

Reputation: 972

Assuming the calculation of parent width isn't dynamic (the resizing of the child dosent affect the parent) and width is constant (big if's).

var width = $(this).parent().width();

$('table.Grid tbody td.GridCell > div').each(function() {
    $(this).css('width', width - 3);
});

Upvotes: 0

Related Questions