Best way to optimize jQuery selector and why? - javascript

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.

Related

Identifying elements in messy (but predictable) HTML code

I want to develop a very simple userscript for a web app, which will mainly insert some elements and CSS. I can't make changes to the source code. The HTML code is kind of messy: It's table-based, most elements don't have IDs, not all of them have useful classes, there are lots of useless elements nested inside other elements, etc. In short, no element is clearly identifiable.
The good things are that the layout is always the same, so the structure is predictable, and there will be no changes to the source code for the foreseeable future (probably never). Taking these things into consideration, the only solution I could think of was to identify the elements through their messy context (for example, one element of interest can be identified by $("div table table > tr > td");). If I follow this idea, things will work, but it won't exactly radiate quality.
So, is there a better way to achieve this?
If you are wanting to relieve a bit on code quality, by means of not having a whole lot of selectors with a many child elements, putting the selections you would like into some variables to refer to. For example:
Instead of having to write
$("div table table > tr > td").on("event", function(){ code });
Save the selectors into a variable, especially if they are used more than once.
var $a_Variable = $("div table table > tr > td");
And then reference the variable as a selector. You could also add your classes to them to if you cannot edit your html code, granted if you have access to define css styling.
$(a_Variable).on("click", function(){ $(this).addClass('.some_class'); });
Defining variables is a good way to save on rewriting the same thing multiple times.
I recommend watching this talk on "effort-less styles", which could also be used for js. https://vimeo.com/101718785
Here is an article about the same topic http://www.smashingmagazine.com/2013/08/20/semantic-css-with-intelligent-selectors/

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');}});

Performance in jQuery with selectors

i was wondering things...
If i need to get the content or append an click function to an div, as the structure of the selectors it's something like that:
$('body #content #sidebar .modalwindow #global-content')
i want to target #global-content, the final id of the selectors.
what its better?
Just target it as $('#global-content') and do what i wanna or give to it all the path?
$('#global-content') is the best selector to use, altough maybe the whole selector will be executed the same way (if jQuery starts from right to left, which I'm not sure it does). ID should be unique and getElementById() is the fastest native browser method, so $('#global-content') is the fastest possible selector.
Keep in mind also, that when you are searching for something exactly 1 level lower in the DOM tree, you can put > in the selector. Example:
$('body .content') is equialent to $('body').find('.content')
$('body > .content') is equialent to $('body').children('.content')
The second one is faster.
You can experiment and try out your selectors here
a similar question was asked in
jQuery Selectors, efficiency
the answer is that
$('#global-content')
is faster
if you know the id of your element and if your id is really unique (as it should be). It is faster to call directly the id >> $('#global-content').
Thus, it is interpreted by jquery to one of the fastest selector getElementById() instead of filtering the DOM.
Note: I know jquery 1.5 and higher (maybe even since 1.4) were optimized to select by id even if the jquery code was adding too much information but that's not the best way to rely on the framework to correct a bad coding

jQuery Selectors, efficiency

I have been reading more lately about the efficiency of the different selector engines. I know that jQuery uses the Sizzle engine and this blog post about some jQuery stuff mentioned that the Sizzle engine will break apart your selector into an array then parse left to right.
It then, from right to left, begins deciphering each item with regular expressions. What this also means is that the right-most part of your selector should be as specific as possible — for instance, an id or tag name.
My question is whether it is more efficient to run a selector with just the ID specified or the tag name as well:
var div = $('#someId');
//OR
var div = $('div#someId');
Since I write my CSS in the div#someId form I tend to do my selectors the same way, am I causing Sizzle to perform extra work (assuming QuerySelectorAll is unavailable)?
jQuery and Sizzle optimize the #id selector [source] to document.getElementById(id). I think they aren't able to optimize it like this with the tag#id.
The first is faster.
BTW specifying the tag name for an #id selector is over-specifying, as there can be only one tag with a given id on the document. Over-specifying is slower even in CSS.
Rather than speculating, let's measure it!
Here's a test case with this page loaded, then matching a random element with both methods.
Make sure you scroll right down to the bottom.
http://jsperf.com/which-jquery-sizzle-selector-is-faster#runner
As you might expect, a plain id is faster than a tag qualified one (reduction to getElementByID). This is the same when using classes.
Selecting by ID is massively faster than selecting by class, mainly as IDs are guaranteed to be unique.
If you are using jQuery, you can assume a browser with getElementById. $('#someId') can be converted to document.getElementById('someId'). $('div#someId') won't be, so it will be faster to lose the tag name.
jsPerf demonstrating this. The difference is enormous.
var div = $('#someId'); //should be faster
jQuery will use getElementById() for the above selector
var div = $('div#someId'); //would probably preform slower due to finding all the divs first
jQuery will use getElementsByTagName(), then iterate though the array of elements for the above selector
You should also remember, tag names should definately be used with class selectors (whenever possible)
var div = $('div.myclass') //faster than the statement below
versus
var div = $('.myclass') //slower
JsPerf: http://jsperf.com/jquery-id-vs-tagname-id
The first one is going to be faster because it only has to check the id. The second one runs the same check AND has to make sure the tagname is correct. div#id won't give you the element with id id unless it is a div
You can see from the source code here: http://code.jquery.com/jquery-1.6.2.js in the function init.
The fastest selector is $('') which just returns an empty jQuery object immediately.
$('body') is next, which jQuery converts to document.body
The next is $('#id') which uses document.getElementById('id').
Any other selector such as $('div#id') will just become a call to $(document).find('div#id'). find() is very complex compared to any of those other solutions and uses Sizzle JS to select the div.

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