views:

36

answers:

3

As the title states. I'm using jQuery to do the magic. I've used a custom Contains extension to the selectors as follows:

jQuery.expr[':'].Contains = function(a, i, m) {
   return jQuery(a).text().toUpperCase().indexOf(m[3].toUpperCase()) >= 0;
};

which I found on the interwebs. It works fine. I'm using it in conjunction with the following:

$("#txtSurname, #txtForename").keyup(function() {
    var forenameVal = $("#txtForename").val();
    var surnameVal = $("#txtSurname").val();
    $("#tblEmployees tr").show();
    if (forenameVal.length > 0) { $("#tblEmployees tr td:nth-child(1):not(:Contains('" + forenameVal + "'))").parent().hide(); }
    if (surnameVal.length > 0) { $("#tblEmployees tr td:nth-child(2):not(:Contains('" + surnameVal + "'))").parent().hide(); }
});

However, this is highly inefficient and with a table of 500 rows it struggles tremendously. My jQuery uber-ninja-skills aren't as great as the next developer when it comes to writing efficient selectors, thus I was wondering if there's a better way to do this?

+2  A: 

One suggestion is to reuse the common jQuery object, which should cut down on overhead:

$("#txtSurname, #txtForename").keyup(function() {
    var forenameVal = $("#txtForename").val();
    var surnameVal = $("#txtSurname").val();
    var  t = $("#tblEmployees tr");
    t.show();
    if (forenameVal.length > 0) { t.find("td:nth-child(1):not(:Contains('" + forenameVal + "'))").parent().hide(); }
    if (surnameVal.length > 0) { t.find("td:nth-child(2):not(:Contains('" + surnameVal + "'))").parent().hide(); }
});
Justin Swartsel
Still pretty damn slow unfortunately, although the suggestion makes sense.
Kezzer
have you tried taking out the ":not(:Contains" selector just to see if that's where the bottleneck is?
Justin Swartsel
A: 

Have you considered refining your custom selector (see the following)

http://stackoverflow.com/questions/1940471/how-do-i-determine-an-html-input-element-type-upon-an-event-using-jquery

http://stackoverflow.com/questions/1940574/what-useful-custom-jquery-selectors-have-you-written

var $item = jQuery(a);
return ($item.attr("type") == "input") &&
        $item.text().toUpperCase().indexOf(m[3].toUpperCase()) >= 0;
James Wiseman
+1  A: 

This is rather a hard selector for jQuery to match. You are asking it to do quite a lot of unnecessary work.

Whereas standard CSS selectors can allow you to leverage the browser's fast CSS matching implementation in newer browsers that have querySelectorAll, custom jQuery selectors are always going to be slow. I would suggest that if you are having speed problems you would probably be better off writing the matching code as explicit JavaScript yourself rather than shoehorning it into selectors.

Using explicit matching code also means you can lose the ugliness of the string concatenation into the selector Contains('" + forenameVal + "'), which will go wrong when special chars such as ' are present in the input name. ' is quite often present in real-world names.

eg. (untested):

var forenameVal= $('#txtForename').val().toUpperCase();
var surnameVal= $('#txtSurname').val().toUpperCase();
var table= document.getElementById('table');
for (var i= table.rows.length; i-->0;) {
    var row= table.rows[i];
    row.className= (
        row.cells[0].firstChild.data.toUpperCase().indexOf(forenameVal)!==-1 &&
        row.cells[1].firstChild.data.toUpperCase().indexOf(surnameVal)!==-1
    )? '' : 'hidden';
}

Note this relies on each name cell having a single Text node child to get the data from. If that's not the case (a name cell may contain other content, or no content at all, not even whitespace) you would have to use $(row.cells[0]).text() instead, but this will also be slower.

It also relies a className to key hiddenness (you'd put .hidden { display: none; } in the stylesheet) to avoid some of the difficulties with hiding table rows. You could instead turn it into an if (...) $(row).show(); else $(row).hide(); but again this makes jQuery do a little more work.

bobince