views:

416

answers:

4

Since I use jQuery 1.3+ all except one timed test is using that. The other is plain javascript I found from back in 2000. I stopped going that route as it was taking around 150 seconds to run the test. I've read quite a few jQuery optimization web pages that relate to selecting a single element. A '#id' is the best case for using that, but now I have the issue of checking all checkboxes in one column in a rather large table that has multiple checkbox columns.

What I have done is setup a page the creates 20,000 table rows with two check box columns. The goal is to check the second column see how long that took, and then uncheck them and see how long that took. Obviously we want the lowest time. I'm only using IE6 and 7 and in my case all of my users will be doing the same.

20,000 rows you say? That's what I said too, but this is going to production (out of my hands) and it's too late to change now. I'm just trying to throw a hail mary with 1 second left on the clock. Besides, I've learned that input.chkbox isn't the fastest selector (for IE7)! :)

The question is, is there a better way to do this jQuery or otherwise? I'd love it to be running in less than half a second on my machine.

So you don't have to retype all the crap I've already done here's the test stuff I came up with:

Updated Morning 4/14 to include further time trials:

<form id="form1" runat="server">
<div>     
  <a href="#" id="one">input[id^='chkbox'][type='checkbox']</a><br />
  <a href="#" id="two">#myTable tr[id^='row'] input[id^='chkbox'][type='checkbox']</a><br />
  <a href="#" id="three">#myTable tr.myRow input[id^='chkbox'][type='checkbox']</a><br />
  <a href="#" id="four">tr.myRow input[id^='chkbox'][type='checkbox']</a><br />
  <a href="#" id="five">input[id^='chkbox']</a><br />
  <a href="#" id="six">.chkbox</a><br />
  <a href="#" id="seven">input.chkbox</a><br />
  <a href="#" id="eight">#myTable input.chkbox</a><br />

  <a href="#" id="nine">"input.chkbox", "tr"</a><br />
  <a href="#" id="nine1">"input.chkbox", "tr.myRow"</a><br />
  <a href="#" id="nine2">"input.chkbox", "#form1"</a><br />
  <a href="#" id="nine3">"input.chkbox", "#myTable"</a><br />

  <a href="#" id="ten">input[name=chkbox]</a><br />
  <a href="#" id="ten1">"input[name=chkbox]", "tr.myRow"</a><br />
  <a href="#" id="ten2">"input[name=chkbox]", "#form1"</a><br />
  <a href="#" id="ten3">"input[name=chkbox]", "#myTable"</a><br />

  <a href="#" id="ten4">"input[name=chkbox]", $("#form1")</a><br />
  <a href="#" id="ten5">"input[name=chkbox]", $("#myTable")</a><br />

  <a href="#" id="eleven">input[name='chkbox']:checkbox</a><br />
  <a href="#" id="twelve">:checkbox</a><br />
  <a href="#" id="twelve1">input:checkbox</a><br />
  <a href="#" id="thirteen">input[type=checkbox]</a><br />

  <div>
   <input type="text" id="goBox" /> <button id="go">Go!</button>
   <div id="goBoxTook"></div>
  </div>

  <table id="myTable">
   <tr id="headerRow"><th>Row #</th><th>Checkboxes o' fun!</th><th>Don't check these!</th></tr>
   <% for(int i = 0; i < 20000;i++) { %>
   <tr id="row<%= i %>" class="myRow">
    <td><%= i %> Row</td>
    <td>
     <input type="checkbox" id="chkbox<%= i %>" name="chkbox" class="chkbox" />
    </td>
    <td>
     <input type="checkbox" id="otherBox<%= i %>" name="otherBox" class="otherBox" />
    </td>
   </tr>
   <% } %>
  </table>
</div>
  <script type="text/javascript" src="<%= ResolveUrl("~/") %>Javascript/jquery.1.3.1.min.js"></script>
  <script type="text/javascript">

   $(function() {     
    function run(selectorText, el) {     
     var start = new Date();      
     $(selectorText).attr("checked", true);        
     var end = new Date();
     var timeElapsed = end-start;
     $(el).after("<br />Checking Took " + timeElapsed + "ms");

     start = new Date();      
     $(selectorText).attr("checked", false);        
     end = new Date();
     timeElapsed = end-start;
     $(el).after("<br />Unchecking Took " + timeElapsed + "ms");
    }  

    function runWithContext(selectorText, context, el) {     
     var start = new Date();      
     $(selectorText, context).attr("checked", true);        
     var end = new Date();
     var timeElapsed = end-start;
     $(el).after("<br />Checking Took " + timeElapsed + "ms");

     start = new Date();      
     $(selectorText, context).attr("checked", false);        
     end = new Date();
     timeElapsed = end-start;
     $(el).after("<br />Unchecking Took " + timeElapsed + "ms");
    }

    $("#one").click(function() {      
     run("input[id^='chkbox'][type='checkbox']", this);
    });

    $("#two").click(function() {
     run("#myTable tr[id^='row'] input[id^='chkbox'][type='checkbox']", this);
    });

    $("#three").click(function() {
     run("#myTable tr.myRow input[id^='chkbox'][type='checkbox']", this);
    });

    $("#four").click(function() {
     run("tr.myRow input[id^='chkbox'][type='checkbox']", this);
    });

    $("#five").click(function() {
     run("input[id^='chkbox']", this);
    });

    $("#six").click(function() {
     run(".chkbox", this);
    });

    $("#seven").click(function() {
     run("input.chkbox", this);
    });

    $("#eight").click(function() {
     run("#myTable input.chkbox", this);
    });

    $("#nine").click(function() {
     runWithContext("input.chkbox", "tr", this);
    });


    $("#nine1").click(function() {
     runWithContext("input.chkbox", "tr.myRow", this);
    });
    $("#nine2").click(function() {
     runWithContext("input.chkbox", "#form1", this);
    });
    $("#nine3").click(function() {
     runWithContext("input.chkbox", "#myTable", this);
    });

    $("#ten").click(function() {
     run("input[name=chkbox]", this);
    });     

    $("#ten1").click(function() {
     runWithContext("input[name=chkbox]", "tr.myRow", this);
    });

    $("#ten2").click(function() {
     runWithContext("input[name=chkbox]", "#form1", this);
    });

    $("#ten3").click(function() {
     runWithContext("input[name=chkbox]", "#myTable", this);
    });

    $("#ten4").click(function() {
     runWithContext("input[name=chkbox]", $("#form1"), this);
    });

    $("#ten5").click(function() {
     runWithContext("input[name=chkbox]", $("#myTable"), this);
    });

    $("#eleven").click(function() {
     run("input[name='chkbox']:checkbox", this);
    });

    $("#twelve").click(function() {
     run(":checkbox", this);
    });

    $("#twelve1").click(function() {
     run("input:checkbox", this);
    });

    $("#thirteen").click(function() {
     run("input[type=checkbox]", this);
    });

    $('#go').click(function() {
     run($('#goBox').val(), this);
    });
   });
  </script>
</form>
+5  A: 

I haven't tested this, but you could try building an array[] of checkbox references on page load then simply iterate over that each time you want to make a change?

You'd pay the performance cost on page load, but it might be quicker than walking the DOM each time. Hey, at least you'd be performing the heavy lifting in user 'down time' (how long does it take for people to locate and click the unselect/select options).

Codebrain
Or I wonder if you could "cache" them into an array the first time (or at load), and then it'd be quicker when they hit it again. Cool idea, I'll give it a go.
rball
let me know how you get on!
Codebrain
Will do, but might be tomorrow, as the work day has almost ended.
rball
My biggest issue with this is that they could very well never click on the select all. With 20k rows it actually locks the browser for a good 2 seconds. I'm still going to give it a go, but that might be a problem.
rball
If you are worried about the initial loading locking a UI thread you could push it onto a seperate thread using window.setTimeout.
Codebrain
I'll have to look up how to do that.
rball
+4  A: 

input[name=chkbox] is coming in as the fastest jQuery selector on my machine under IE7.

Unchecking Took 2453ms
Checking Took 2438ms
Unchecking Took 2438ms
Checking Took 2437ms
Unchecking Took 2453ms
Checking Took 2438ms

input.chkbox and...

Unchecking Took 2813ms
Checking Took 2797ms
Unchecking Took 2797ms
Checking Took 2797ms
Unchecking Took 2813ms
Checking Took 2797ms

input:checkbox.chkbox seem tied

Unchecking Took 2797ms
Checking Took 2797ms
Unchecking Took 2813ms
Checking Took 2781ms

.chkbox almost takes twice as long as the input.chkbox

Unchecking Took 4031ms
Checking Took 4062ms
Unchecking Took 4031ms
Checking Took 4062ms

The javascript for loop is by far the worst coming in at:

Checking Took 149797ms

150 seconds! It locks the browser too. This just makes me really impressed with jQuery. I honestly didn't expect it to be that slow. Probably because I'm passing across each individual element which it's then having to find...

This was pretty interesting to me as well:

input[id^='chkbox']

Unchecking Took 3031ms
Checking Took 3016ms

took less time than:

input[id^='chkbox'][type='checkbox']

Unchecking Took 3375ms
Checking Took 3344ms

I thought since I posted more filters it'd be faster. Nope!

Specifying even more paths to the checkbox makes it way slower:

#myTable tr[id^='row'] input[id^='chkbox'][type='checkbox']

Checking Took 10422ms

It didn't even run the second uncheck as it asked me if I wanted to continue running scripts on my computer. Crazy! :P

Update Morning 4/14:

Someone brought up setting the context: I actually did a few of those and much to my suprise and against what a lot of people have said on the web on IE7 these were slower! Here are the times I got with a few different context's specified paired with the quicker selector's above:

"input.chkbox", "tr"

Checking Took 8546ms

"input.chkbox", "tr.myRow"

Checking Took 8875ms

"input.chkbox", "#form1"

Unchecking Took 3032ms
Checking Took 3000ms

"input.chkbox", "#myTable"

Unchecking Took 2906ms
Checking Took 2875ms

Current winner (still): input[name=chkbox]

Unchecking Took 2469ms
Checking Took 2453ms

"input[name=chkbox]", "tr.myRow"

Checking Took 9547ms

"input[name=chkbox]", "#form1"

Unchecking Took 3140ms
Checking Took 3141ms

"input[name=chkbox]", "#myTable"

Unchecking Took 2985ms
Checking Took 2969ms

Update 2 Morning 4/14

Thought I might have had a better one after I noticed a syntax difference from http://beardscratchers.com/journal/jquery-its-all-about-context. It seems that these are NOT the same as they are giving slightly better times, but still doesn't beat the non-contexted selector - darn.

"input[name=chkbox]", $("#form1")

Unchecking Took 3078ms
Checking Took 3000ms
Unchecking Took 3078ms
Checking Took 3016ms

"input[name=chkbox]", $("#myTable")

Unchecking Took 2938ms
Checking Took 2906ms
Unchecking Took 2938ms
Checking Took 2921ms

Update 3 Morning 4/14

Russ wanted me to try these out, they de/select ALL the boxes but again it was interesting:

:checkbox

Unchecking Took 8328ms
Checking Took 6250ms

input:checkbox

Unchecking Took 5016ms
Checking Took 5000ms

-> Fastest?!?! input[type=checkbox]

Unchecking Took 4969ms
Checking Took 4938ms

The fact that the third up there is the fastest is quite interesting as that goes against what I would have thought. Why wouldn't (for IE7 at least) the :checkbox just use the type=checkbox to achieve a faster time? These are really close scores but the checking took 62ms less time. Also, why are the first two different at all? Is there a different element besides an input that can take have a checkbox?

rball
More selection criteria do not speed up a query unless each one reduces the set size. Since all the checkboxes are in table rows and have input, adding those as criteria doesn't help.
Zan Lynx
"I thought since I posted more filters it'd be faster." as Zan said, more filters = more checks = more processing = more time. Unless each filter significantly reduces the set size for the next filter, it's just taking up cycles.
nickf
+2  A: 

My only suggestion probably wont work either. Switch browsers. But I've only had one company actually agree to that. We had the company switch to FireFox, and specific users move to Google Chrome. IE is just too slow with JavaScript.

Also, you can try pre-caching the jquery query list.

If all else fails, solve it with psychology. That means letting the user know that something is going to take a long time. Put up a "Please wait" div while the function is performing. That way the user knows that the browser isn't just locked up, and they know when they can get back to work. I have had many a slow page "solved" by doing just this.

Chris Brandsma
Lol, yeah just not going to happen. I'm "lucky" enough to be able to use IE7. Everyone else is required to use IE6.I'll look into pre-caching.
rball
Another option...You are using the JQuery attribute manipulation for setting checked:$(selectorText).attr("checked", false); You could also try$(selectorText).each(function(){ this.checked = false });No guarantees, but it might be worth a shot.
Chris Brandsma
Thought of doing that. I'll give it a try and see what happens. We are actually putting up a notification of please wait, before it was 150 seconds, but getting it down to 2-3 is going to be great.
rball
+1  A: 

Have you tried the jQuery selectors with a context to see if that improves performance? Presumably the controls will be inside of an ASP.NET form, and perhaps another uniquely identifiable element?

For example, where you have

$("input[id^='chkbox']")

Try with

$("input[id^='chkbox']", "#myFormID")

Here's a BeardScratchers article on context

EDIT:

Following your updates, it seems like 2.45-6 seconds might be the fastest that you can achieve, given your circumstances.

Just for completeness, have you tried the following selectors?

$(':checkbox')
$('input[type=checkbox]')
Russ Cam
Seems to be slower. Maybe I haven't gotten the "right" context yet though...
rball
Updated post with results.
rball
Wait...read full article, looks like I may have been providing the context wrong??
rball
Worth a try nonetheless. It seems like 2.45-6 seconds might be the fastest you can achieve.
Russ Cam
Looks like it at this point :) It's actually better than it was, before they were using the javascript method. With those two selectors it'd select all of the checkboxes not just the 2nd column.
rball
I figured, what the hay, I'll try those out too. Again very interesting!
rball