Before I dive in to the jQuery/Sizzle source I thought i’d ask here about ways to speed the below method up.
This is a standard “Check All” checkbox scenario. A header cell (<thead><tr><th>) has a checkbox that when checked checks all other checkboxes in it’s table’s tbody that are in the same column.
This works:
// We want to check/uncheck all columns in a table when the "select all"
// header checkbox is changed (even if the table/rows/checkboxes were
// added after page load).
(function () {
// use this dummy div so we can reattach our table later.
var dummy = $("<div style=display:none />");
// hook it all up!
body.delegate(".js_checkAll", "change", function () {
// cache selectors as much as possible...
var self = $(this),
// use closest() instead of parent() because
// the checkbox may be in a containing element(s)
cell = self.closest("th"),
// Use "cell" here to make the "closest()" call 1 iteration
// shorter. I wonder if "parent().parent()" would be faster
// (and still safe for use if "thead" is omitted)?
table = cell.closest("table"),
isChecked,
index;
// detaching speeds up the checkbox loop below.
// we have to insert the "dummy" div so we know
// where to put the table back after the loop.
table.before(dummy).detach();
index = cell.index();
isChecked = this.checked;
// I'm sure this chain is slow as molasses
table
// get only _this_ table's tbody
.children("tbody")
// get _this_ table's trs
.children()
// get _this_ table's checkboxes in the specified column
.children(":eq(" + index + ") :checkbox")
// finally...
.each(function () {
this.checked = isChecked;
});
// put the table back and detach the dummy for
// later use
dummy.before(table).detach();
});
} ());
However, for 250+ rows, it starts to become slow (at least on my machine). Users may need to have up to 500 rows of data so paging the data isn’t the solution (items are already paged @ 500/page).
Any ideas how to speed it up some more?
No. If
<thead>is omitted then in HTML a<tbody>element will be automatically added, because in HTML4 both the start-tag and the end-tag are ‘optional’. So in HTML, it would beparent().parent().parent(), but in XHTML-served-as-XML, which doesn’t have the nonsense that is optional tags, it would beparent().parent().It’s probably best to stick with
closest(). It’s clearer, it’s not particularly slow and you’re only using it once, so it’s not critical anyway.Although, again, this is only once per table so not critical, there is a standard DOM property to get the index of a table cell directly, which will be faster than asking jQuery to search and count previous siblings:
index= cell[0].cellIndex.That’s a bit ugly. Standard DOM has a better answer to this: remember the element’s
parentNodeandnextSibling(which may benullif this is the last sibling) and when you’re done you canparent.insertBefore(table, sibling).You should consider using
.children().eq(index)rather than hiding that away in a selector. Won’t make a big difference, but it’s a bit clearer.In any case, you can save jQuery’s selector engine a bunch of work by using some more standard DOM to traverse the table:
Selector queries can be fast, when they’re operating against the document and using only standard CSS selectors. In this case jQuery can pass the work onto the browser’s fast
document.querySelectorAllmethod. But scoped selectors (findand the second argument to$()) can’t be optimised due to a disagreement between jQuery and Selectors-API over what they mean, and non-standard Sizzle selectors like:eqand:checkboxwill just get rejected. So this:could actually be faster, on modern browsers with
querySelectorAll!