DOM manipulation uses native functions to perform simple operations. Browser vendors optimize these. You are building the row from HTML. Internally jQuery is using .innerHTML
to build the collection which then patches into the browser's mega-fast parser.
Selection is slow in comparison because JS code needs to loop through the DOM repeatedly. Newer browsers have native selection handling which provides dramatic speedups to selector based JS. As time moves on this will be less of a problem.
Here is how the query in question, $(".checkbox input", row)
, breaks down:
row.getElementsByTagName('*');
- for-loop through every element returned (all elements within the row) and test
elements[i].className
with /(\s|^)checkbox(\s|$)/
.
- for-loop every element still remaining and collect
matched[i].getElementsByTagName('input');
- unique the final collection.
This is different for jQuery 1.3 as it's engine moves through the selector the other way around, beginning with getting all input elements and then testing the parent elements.
Rremember that the JS selector engines implement a lot more of the CSS selector spec than is actually usable with CSS (or implemented by current browsers). Exploiting this, and knowledge of the engines, we can optimize selector can be optimized in a few different ways:
If you know what element type the .checkbox
is:
$("td.checkbox input", row);
It is faster for filter first for type and then for the class for only those matches. This doesn't apply for a very small subset of elements, but that is almost never the case in praxis.
The single class test is the slowest of the common selectors people actually use.
Simpler selection:
$("input[type=checkbox]", row);
One loop is faster than two loops. This only finds input elements and then directly filters them by type attribute. Since sub/child-elements are never used, unique may also be skipped (and smart engines will try to do this because unique is slow).
A more direct selector:
$("td:first.checkbox input", row);
A more complex selector may actually be faster if it is more direct (YMMV).
If possible, move the search context up to the table level:
By this I mean that instead of looping through the rows, and searching for the checkbox in every one, leave them alone until after the loop and then select them all at a time:
$("tr td:first.checkbox input", table);
The point of this is to eliminate the overhead of firing the selector engine up repeatedly, but instead do everything in one haul. This is presented here for completeness rather than something that I think would return massive speedups.
Don't select:
Build the row from bits, assigning events as you go.
var row = $( '<tr></tr>' );
var cell = $( '<td class="checkbox"></td>' ).appendTo( row );
$( '<input type="checkbox" name="..."/>' ).appendTo( cell ).click(/* ... */);
This may be impossible for reasons of Ajax or other templates out of your control. Additionally, the speed may not be worth turning your code into this sort of mess, but sometimes this may make sense.
Or, if none of these work for you, or return too performance gain, it may be time to rethink the method entirely. You can assign an event listener higher up the tree and grab the events there, instead of per-element instance:
$('table').change(function(e){
// you may want a faster check...
if ( $(e.target).is('input[type=checkbox]') ) {
// do some stuff ...
}
});
This way you don't do anything unless, and until, the user actually requests it. Fastest. :-)