14

I am using this jQuery selector multiple times in my JSP:

 $("#customers tbody tr td input[type='checkbox'][name='selectedCustomers']")

The solution I found on some blogs is that I should do first:

var customer=$('#customers')

And then use the above customer object for further calls.

 customer.find("tbody tr td input[type='checkbox'][name='selectedCustomers']")

My question is, does this solution will make any difference and why?

My understanding

When I do

$("#customers tbody tr td input[type='checkbox'][name='selectedCustomers']")

jQuery internally will first get the object associated with div id="customers" (by document.getElementById("customers")) and then will traverse to the specified checkbox. And if I go by the suggested solution then document.getElementById("customers") will be fired only once and the rest will be the same. So I am saving myself from unnecessary multiple document.getElementById but the rest will be the same. Is my understanding correct? If yes is, just for the sake of my knowledge, is document.getElementById a more costly operation?

EDIT:-

i am not using only above said selector multiple times but also other possible selector under div id="customer". So question again is whats is difference in terms of performance if I cache the customer object first and if i don't do it?

Jimmy Kane
  • 16,223
  • 11
  • 86
  • 117
M Sach
  • 33,416
  • 76
  • 221
  • 314
  • 4
    If the matched elements are not going to change you should *cache* the whole selector not only `#customers`, after all the problem are the non-supported selectors but that's browser dependent – Alexander Dec 22 '12 at 15:13
  • I don't think that there is no difference other than the second way is better for reading. – SaidbakR Dec 22 '12 at 15:13
  • 2
    @sємsєм caching even the parent certainly improves performance. Further searchs are contained to that object, not full DOM – charlietfl Dec 22 '12 at 15:16
  • @charlietfl: They're not constrained to the full DOM long enough for it to make much of a difference, at least on modern browsers... http://jsperf.com/caching-the-parent Caching a more specific parent would be nicer, if possible. – Ry- Dec 22 '12 at 15:37
  • @minitech certainly caching the whole collection is much better, but testing in Firefox and older IE does show better results for parent cache over no-cache – charlietfl Dec 22 '12 at 15:48
  • 1
    @charlietfl: The maximum difference is about 600 ops/sec, not really as significant as some of the other improvements that can be made, is all. – Ry- Dec 22 '12 at 15:50
  • @minitech agree... it is more about educating about the overall caching concept though instead of OP searching each time. And not using every level of DOM tree in selector – charlietfl Dec 22 '12 at 15:52

4 Answers4

6

There is no way you need to be that specific. I'm guessing, at the very most, this:

$('#customers td [name="selectedCustomers"]')

... which should improve performance. Next, if you're actually querying for selectedCustomers each time, you should cache the whole thing:

var selectedCustomers = $('#customers td [name="selectedCustomers"]');

Otherwise, if the name is dynamic, and you only have one item with the same name per page...

var item = $(document.getElementsByName(someName)[0]);

Caching just $('#customers'), on the other hand, is pretty much pointless. .find on customers will do just as much work as the whole selector in the first place, especially with querySelector support.

Ry-
  • 218,210
  • 55
  • 464
  • 476
  • You said "Caching just $('#customers'), on the other hand, is pretty much pointless. .find on customers will do just as much work as the whole selector in the first place, especially with querySelector support". Does it mean if i do $("#customers tbody tr td input[type='checkbox'] second time , DOM will not be traversed to locate the div id="customers"? If yes, how jquery second time will get the object associated with div id="customer", will it internally cache it? – M Sach Dec 22 '12 at 16:10
  • @MSach: I'm saying that it *will* be traversed a second time either way. As long as you're calling jQuery with a selector, there will be a query. But if you cache something more specific, then the performance difference could matter. (See the jsPerf in the comments above.) – Ry- Dec 22 '12 at 17:50
  • Thanks minitech. Let me clear my doubt by asking what i actually want :). Here it is. If i do $("#customers someSelector"), Dom traversal will only start once jquery starts looking for selector not for #customers(as its an id and can be directly obtained from table/Map prapared by browser. So no need to traverse dom for id). Right? This is the thing iam trying to ask. If answer to this is yes,Then i agree with you whether i cache object associated with id or not, its kind of pointless because its going to be O(1) operation in either case – M Sach Dec 23 '12 at 09:18
  • @MSach: The implementation details are left up to the browser, but yes, ID lookups are "fast". You can and should still cache `$("#customers")` if that’s the absolute best you can cache, though, if just to make the code look a little nicer! But try to aim for something more specific if possible. – Ry- Dec 23 '12 at 15:01
3

You seem to be missing the fundamental point of caching the object. Once the object is cached, any further traversal or manipulation within that selector will be performed on the stored object and doesn't require a search of the DOM to first locate the selector and create the collection every time you need to use it

Every time you call $("#customers tbody tr td input[type='checkbox'][name='selectedCustomers']") a search of the document has to be performed to create the collection of elements before any changes can be made to the collection.

Caching the collection means no further searches need to be made therefore improving performance

/* locate and store the collection once*/
var $checkboxes=$("#customers tbody input[name='selectedCustomers']");
/* look within previously stored collection*/
$checkboxes.filter(/* expression*/ ).doSomething();

Using document.getElementById will be faster than a jQuery search, simply because it doesn't require addiitonal function calls made by jQuery library. However if you wish to use result as a jQuery object like: $( document.getElementById('foo')) the gains are likely not worth worrying about for a single use to cache an object

charlietfl
  • 170,828
  • 13
  • 121
  • 150
  • You mean if i do $("#customers tbody tr td input[type='checkbox'] second time , again DOM has to be traversed to first locate the div id="customers" Right? On the other hand if i do cache the object associated with div #customers after first time,that traversal will be saved. Is that correct? – M Sach Dec 22 '12 at 15:50
1

So I am saving myself from unnecessary multiple document.getElementById but the rest will be the same.

Yes. But maybe also no, as selectors are evaluated from right to left (see this article or this SO question). And assuming an efficient engine, it had less work to do if it does that evaluation only on a part of the document tree if you first select #customers and then .find() in it. But I'm not 100% sure about that.

Is document.getElementById a more costly operation?

No, it is very cheap. Ids are the standard attribute to identify single elements, and browsers will build very performant lookup tables for it - you can assume it to be nearly O(1).

customer.find("tbody tr td input[type='checkbox'][name='selectedCustomers']")

On the other hand, DOM selector queries which need to evaluate the DOM tree are very costly, especially if done manually in JS code (jQuery sizzle) and not native - though this rather simple query will be delegated to the native querySelectorAll.

I am guessing that #customers is your table element. So for performance, omit the tbody tr td tags, they are obligatory (assuming you have not used them to explicitly exclude checkboxes from <thead>/<tfoot> or <th> elements). You will not find an <input> as a direct child of a table element anyway - and the selector engine has much less to do.

Further, if you know your markup well and can make the assumption that only checkboxes have that name attribute, you might omit the tagname and type attribute selectors as well. And that means you can delegate to the native getElementsByName, which should boost performance a little bit again:

$(document.getElementById("customers").getElementsByName("selectedCustomers"))

If you need to check for the elements to be checkboxes, you still could filter them. With that, you might end up with

$(customer.get(0).getElementsByName("selectedCustomers")).filter(":checkbox")

However, to proof the performance gains you only can test, test, test; and you'll need to do that on your actual full page.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thanks Bergi.Let me clear my doubt by asking what i actually want :). Here it is. If i do $("#customers someSelector"), Dom traversal will only start once jquery starts looking for selector not for #customers(as its an id and can be directly obtained from table/Map prapared by browser. So no need to traverse dom for id). Right? – M Sach Dec 23 '12 at 09:16
  • would be helpful if you can shed some light on above comment – M Sach Dec 31 '12 at 10:15
0

http://jsperf.com/different-jquery-selector-tests

Check out this little test. Basically $('#div').find('#p'); is the fastest and $('div').find('#p'); is the slowest.

Webmaster G
  • 502
  • 5
  • 12