JQuery selector performace and comparesion issue - javascript

All three operation are performing same thing.
can anyone explain the reason for different performance?
console.time('first');
for(guid in ALLGUID){
$('.'+guid).find('.cell-taskname').find('.cell-text').text()
}
console.timeEnd('first');
console.time('second');
for(guid in ALLGUID){
$('.'+guid).find('.cell-taskname .cell-text').text()
}
console.timeEnd('second');
console.time('third');
for(guid in ALLGUID){
$('.'+guid+' .cell-taskname .cell-text').text()
}
console.timeEnd('third');
first: ~3500.000ms
second: ~3700.000ms
third: ~38000.000ms (yes it is 38 second.)
Scenario Explanation: I have Huge table and I am iterating over all table rows. here Guid is unique key and class name of table row. cell-taskname is div inside every first column of table-row and .cell-text is span containing text.

It all has to do with the fact that selectors are processed right-to-left.
In your first and second examples, the main search of the DOM is only for the elements with the given GUID class. Then, you only search within those elements for the cells.
But in your last example, with a single selector, the engine does this:
Make a list of all .cell-text elements.
Remove ones that don't have an ancestor matching .cell-taskname from the list.
Remove any remaining ones that don't have an ancestor matching .<your-guid-here> from the list.
You can see how that takes a bit longer, if there are lots of .cell-text elements and only a few .<your-guid-here> elements. You've said that the .<your-guid-here> element is unique (a single row in a massive table), so the performance difference makes sense here.
More on right-to-left matching in this question and its answers.

lets say you have 100 rows with GUIDs, with each 100 .cell-taskname in them, with each 100 .cell-text in them.
100 GUIDS
10000 .cell-taskname
1000000 .cell-text
selectors are done right to left, so if you would do the 3rd example:
select all .cell-text (1m)
eliminate those that don't an ancestor .cell-taskname (still 1m)
eliminate those that don't have a higher ancestor of .guid (10k)
while if you do the first one:
select all .guid (1)
find all cell-taskname in that (100)
select all cell-text in those (10000)
in the 3rd (my first) scenario, you work with 2.01m elements
in the 1st (my second) scenario, you work with only 10101 elements. So that is why it's that much faster :-)
of course, these aren't exact performance numbers, but it does explain the concept behind selector optimizations...

Related

Difference between $('#tabs a') and $('#tabs').find('a')

I have following structure
<ul id="tabs" class="nav nav-tabs">
<li>AAA</li>
<li>BBB</li>
<li>CCC</li>
<li>DDD</li>
</ul>
Now I am operating on the anchor tag by following code and which is working fine.
$('#tabs a[href="#ddd"]').tab('show');
I am using pycharm which adds warning for the line by saying "Preface with ID selector". When I click it, pycharm changes to following
$('#tabs').find('a[href="#ddd"]').tab('show');
Both are working fine but I don't understand the difference.
What is the difference in both or more specifically what is difference between $('#tabs a[href="#ddd"]') and $('#tabs').find('a[href="#ddd"]')?
$("#tabs a") evaluates from right to left - which is the native direction of both Sizzle selector engine and querySelectorAll - i.e. first it finds all of the anchor elements in the page and then narrows it down to those under #tabs.
$("#tabs").find("a") evaluates - more intuitively - from left to right, i.e. first it finds #tabs, and then only the anchor elements under it.
Clearly the latter would yield better performance, but it would only be noticeable accumulatively; that is, if you run thousands of queries. Otherwise, the difference is negligible.
As stated in "Increase Specificity from Left to Right":
A little knowledge of jQuery’s selector engine is useful. It works
from the last selector first so, in older browsers, a query such as:
$("p#intro em");
loads every em element into an array. It then works up the parents of
each node and rejects those where p#intro cannot be found. The query
will be particularly inefficient if you have hundreds of em tags on
the page.
Depending on your document, the query can be optimized by retrieving
the best-qualified selector first. It can then be used as a starting
point for child selectors, e.g.
$("em", $("p#intro")); // or
$("p#intro").find("em");
But Test case says $("#tabs > a") would be fastest
The second one is MUCH quicker.
The reason being jQuery's selector enginge Sizzle, which traverses the selection from right to left, not vice versa.
This means that the selector
$('#tabs a[href="#ddd"]')
First queries the DOM document for a tag, which contains the attribute href set to #ddd. It then filteres out all of them, to get every one that is a <a> tag. At last, it traverses up the DOM tree for every node, trying to find a parent #tabs.
Imagine a site with 1.000 tags with href="#ddd", how tremendously slow that would be.
THEN.
The other variation pycharm suggest, is to first locate a element #tabs. This is super-quick, since jQuery can utilize the native browser method getElementById(). Having this node, it can traverse down to find all tags that are matching. By doing this, not all tags in the whole DOM-tree, needs to be checked. Only those which actually are in #tabs.
For further information, please check out this page in the documentation.
The effect is the same: Find anchors that have the value #ddd as href and are a descendant of #tabs. The difference lies in the way to achieve this.
The first solution finds the anchors and then checks if they are a descendant of #tabs.
The second solution finds #tabs and then finds the anchors. Which should be faster, of course.
.find() is better performance wise as compared to your first selector
$('#tabs a[href="#ddd"]').tab('show');
, that is why pycharm changes it to the selector using .find()
$('#tabs').find('a[href="#ddd"]').tab('show');
http://vaughnroyko.com/the-real-scoop-on-jquery-find-performance/
The difference is that find() allows you to filter on a set of elements based on a selection you've already made, returning and array of elements if that's the case.
$('#tabs').find('a[href=“#ddd”]');
And it's a more specific way of searching for an element because you are saying "hey, go to #tabs and find me all a[href=“#ddd”] in there" instead of you saying "hey, find me all this guys $('#tabs a[href=“#ddd”]') in all the code that i have."
While, in most cases, the performance is the only difference, the difference in approach can also affect the outcome of your code, depending on what selectors you are using.
For example, $("table").find("tr:even").addClass("even"); will add the "even" class to the every other row in each individual table that gets returned. So, if the "even" class makes the text in the rows bold and you have two tables, each with 3 rows, you would get the following result:
this is table one, row 1
this is table one, row 2
this is table one, row 3
this is table two, row 1
this is table two, row 2
this is table two, row 3
In both cases, the 1st and 3rd row of each table (i.e., the "even" rows . . . don't get me started on JQuery's even filter, selecting the odd rows . . .) are bolded.
On the other hand, $("table tr:even").addClass("even"); will add the "even" class to every other row in the entire group of rows from all tables combined.
this is table one, row 1
this is table one, row 2
this is table one, row 3
this is table two, row 1
this is table two, row 2
this is table two, row 3
In this situation, the the 1st and 3rd row of second table are actually the 4th and 6th rows of the entire group of <tr> elements, so they are treated as "odd". The 2nd row of the second table, however, is the 5th row of the entire collection and, thus, is treated as "even" and is bolded.

Find closest element in complete document

I have indexed my document using Jquery index Given a certain index value, I would like to 'locate' the 'nearest' element with some class. This will not work for me:
$(variableIndexNumber).closest('.class');
The reason is that sometimes the .class changes its position, meaning it could be anywhere in the document. Sometimes its a sibling, sometimes a parent, sometimes way up in document or in the bottom. So the usual selctors next closest first nextAll etc don't work for me (because of document tree)
What can I try to get the nearest .class relative to some other element (given by variable)?
Use .prevUntil('.class'), .nextUntil('.class'), .parentsUntil('.class'), then compare their sizes.
Whichever one has the least size is the closest to the element in terms of DOM structure.
UPDATE: In the process, you will also need .andSelf(), .filter() and typical .next() and .prev() to actually get to the target you want. Demo has been updated. You can see that now the code is more generic.
You'll probably need a sorting function too, as I've did in the proof-of-concept below.
DEMO: http://jsfiddle.net/terryyounghk/FzA78/
Side-note: There's no .childrenUntil(), because that's branching outwards the DOM tree, not inwards towards the document root, in case you're wondering. But my guess is, you could do a $('.me').find('.class') first, then for each of them, do a .parentsUntil('.me') instead, then compare the sizes. I'm not sure if this would work though.

Best way to optimize jQuery selector and why?

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?
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.
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
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.
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.

Fastest way to hide thousands of <li> elements?

I have an autocomplete form where the user can type in a term and it hides all <li> elements that do not contain that term.
I originally looped through all <li> with jQuery's each and applied .hide() to the ones that did not contain the term. This was WAY too slow.
I found that a faster way is to loop through all <li> and apply class .hidden to all that need to be hidden, and then at the end of the loop do $('.hidden').hide(). This feels kind of hackish though.
A potentially faster way might be to rewrite the CSS rule for the .hidden class using document.styleSheets. Can anyone think of an even better way?
EDIT: Let me clarify something that I'm not sure too many people know about. If you alter the DOM in each iteration of a loop, and that alteration causes the page to be redrawn, that is going to be MUCH slower than "preparing" all your alterations and applying them all at once when the loop is finished.
Whenever you're dealing with thousands of items, DOM manipulation will be slow. It's usually not a good idea to loop through many DOM elements and manipulate each element based on that element's characteristics, since that involves numerous calls to DOM methods in each iteration. As you've seen, it's really slow.
A much better approach is to keep your data separate from the DOM. Searching through an array of JS strings is several orders of magnitude faster.
This might mean loading your dataset as a JSON object. If that's not an option, you could loop through the <li>s once (on page load), and copy the data into an array.
Now that your dataset isn't dependent on DOM elements being present, you can simply replace the entire contents of the <ul> using .html() each time the user types. (This is much faster than JS DOM manipulation because the browser can optimize the DOM changes when you simply change the innerHTML.)
var dataset = ['term 1', 'term 2', 'something else', ... ];
$('input').keyup(function() {
var i, o = '', q = $(this).val();
for (i = 0; i < dataset.length; i++) {
if (dataset[i].indexOf(q) >= 0) o+='<li>' + dataset[i] + '</li>';
}
$('ul').html(o);
});
As you can see, this is extremely fast.
Note, however, that if you up it to 10,000 items, performance begins to suffer on the first few keystrokes. This is more related to the number of results being inserted into the DOM than the raw number of items being searched. (As you type more, and there are fewer results to display, performance is fine – even though it's still searching through all 10,000 items.)
To avoid this, I'd consider capping the number of results displayed to a reasonable number. (1,000 seems as good as any.) This is autocomplete; no one is really looking through all the results – they'll continue typing until the resultset is manageable for a human.
I know this is question is old BUT i'm not satisfied with any of the answers. Currently i'm working on a Youtube project that uses jQuery Selectable list which has around 120.000 items. These lists can be filtered by text and than show the corresponding items. The only acceptable way to hide all not matching elements was to hide the ul element first than hide the li elements and show the list(ul) element again.
You can select all <li>s directly, then filter them: $("li").filter(function(){...}).hide() (see here)
(sorry, I previously posted wrong)
You can use the jQuery contains() selector to find all items in a list with particular text, and then just hide those, like this:
HTML:
<ul id="myList">
<li>this</li>
<li>that</li>
<ul>​
jQuery
var term = 'this';
$('li:contains("' + term + '")').hide();​
You could use a more unique technique that uses technically no JavaScript to do the actual hiding, by putting a copy of the data in an attribute, and using a CSS attribute selector.
For example, if the term is secret, and you put a copy of the data in a data-term attribute, you can use the following CSS:
li[data-term*="secret"] {
display: none;
}
To do this dynamically you would have to add a style to the head in javascript:
function hideTerm(term) {
css = 'li[data-term*="'+term+'"]{display:none;}'
style = $('<style type="text/css">').text(css)
$('head').append(style);
}
If you were to do this you would want to be sure to clean up the style tags as you stop using them.
This would probably be the fastest, as CSS selection is very quick in modern browsers. It would be hard to benchmark so I can't say for sure though.
How about:
<style>
.hidden{ display: none; }
</style>
That way you don't have to do the extra query using $('.hidden').hide() ?
Instead of redefining the Stylesheets rules, you can directly define 'hide'
class property to "display:none;" before hand and in your page, you can just
apply the class you defined after verifying the condition through javascript,
like below.
$("li").each(function(){if(condition){$(this).addClass('hide');}});
and later, if you want to show those li's again, you can just remove the class like below
$("li").each(function(){if(condition){$(this).removeClass('hide');}});

What is the fastest way to get a dom element?

I'm performance-tuning my code, and am surprised to find that the bottleneck is not dom node insert, but selection.
This is fast:
var row = jquery(rowHTML).appendTo(oThis.parentTable);
but the subsequent getting of an element inside "row" is slow:
var checkbox = jquery(".checkbox input", row);
I need to get the checkbox in every row so I can attach an event handler to it. Selecting the checkbox is ALMOST 10X AS SLOW as inserting the entire parent row.
What am I doing wrong here?
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. :-)
var checkbox = jquery(".checkbox input", row);
This is traversing the entire dom tree to find the checkbox. You could possibly speed it up by changing the selector to an ID which can use the browsers native getElementById functionality.
var checkbox = jquery("#checkbox input", row);
You could also use your row as a starting point for the DOM search like the following example. Now your not parsing through the entire DOM tree again to find the matched element.
var row = jquery(rowHTML).appendTo(oThis.parentTable);
row.children(".checkbox input");
Use event delegation and add a single handler to a parent element and not the checkboxes themselves.
jQuery supports this via the live() function.
Try putting a class name on the input field itself. That may prove to be faster.
The reason for that is your code goes through all .checkbox classes, tries to find the input child of that element and returns that. I think that action might be your culprit.
By simply just looking for all elements with the class the input field has, you might see some speedup.
Try using Sly, it has an emphasis on performance.
If you're looking for performance, jQuery selectors are very slow. In the example there, it has to scan the full DOM tree and check CSS classes and so on to find the relevant nodes.
It is significantly faster to use native DOM methods. There are some interesting library performance comparisons here:
http://ajaxian.com/archives/taskspeed-more-benchmarks-for-the-libraries-and-browsers
The fastest way to get the DOM element is to use pure JavaScript and calling it by ID.
var element = document.getElementById('element);

Categories

Resources