HTML markup:
<div>
<a id="foo"> </a>
</div>
jQuery:
$('div').each(function(){
$('#foo', this).dosmth(); // 1
$('#foo').dosmth(); // 2
});
Which method would be faster to run dosmth?
Since we're getting a variety of answers, hopefully here's some clarity (check the examples here):
The fastest - There's no need to loop. Skip the $("div").each part and just do $("#foo"). foo is an ID, and thus lookup is instantaneous.
Middling - $("#foo") in a loop. Note that you also don't want this because it will execute the function for every div on the page (and for this reason on a larger document with a lot of divs this would be the slowest).
Slowest - $("#foo", this). The context node doesn't help in the first place, and then consider that jQuery will first build a jQuery object out of this and turn it into $(this).find("#foo"). That's all unnecessary, of course.
Bottom line: in most cases (e.g. sometimes when confirming that an ID is in one context and not another) context nodes are unnecessary with ID lookup.
Here are some resources from the jQuery source:
Handling for most of the cases here - note that $("#id") is singled out for handling as document.getElementById
find - what happens when you pass a context
Since an #id should be unique in the DOM your markup will be invalid (I am assuming more than one <div/> based upon using .each())
Change the id to a class and use the following:
<div>
<a class="foo"> </a>
</div>
<div>
<a class="foo"> </a>
</div>
And the script
$('div').each(function(){
$('.foo', this).dosmth(); //or $(this).find(".foo");
});
But if you only have one element with an id of foo selecting by id will be the fastest, plus you can drop the need for using .each()
$('#foo').dosmth(); //or document.getElementById("foo");
jquery selectors by id only is the fastest way to search because it uses getElementbyId in javascript.
so this one is the fastest:
$('#foo').dosmth();
if you use a context like:
$('#foo', this).dosmth();
it is translated into:
$(this).find('#foo').dosmth();
so that will make another useless operation because your #foo is unique
Regards,
Max
$('#foo', this).dosmth();
This will search within the context of the div and not the whole DOM, which will make the selector faster. This only makes sense to use when the DOM is large, otherwise, just use the normal selector: $('#foo').dosmth();
If you're using an id there's only ever going to be one. So you can just do:
$('a#foo').dosmth();
You don't need to use each() to go through each div and get all the a#foo's out of it. That WILL waste time, creating loops for no reason. Instead use:
$('a#foo').each(function(){ ... });
or even just:
$('a#foo').dosmth();
You can also do $('div a#foo').dosmth(); if you want.
Please read the discussion below regarding this answer or check Bryan answer above about the differences in speed of the selectors.
I would go with
$('a#foo', this).dosmth();
Update But instead of retrieving all the divs before, I would check for
only the desired one at the first time
like this
$('div a#foo').each(function(){
}
Related
I have several div with the .item class and want to change the css if the id is 1
</div>
<div class="item" id="0">
</div>
$('.item').each(function(i){
var estatus = $(this).attr('id');
if (estatus == '1')
// change only item with id = 1
});
Just modify your selector...
$('#1').css('whatever', 'yep');
Please see the comments section to see why this works in this scenario, but you're gonna have bad time if selecting it in a stylesheet. I'd recommend using the method in T.J.'s answer, because of jQuery ever switch to funnelling these calls through querySelectorAll(), it will blow up).
My recommendation is to not use id attributes that start with a number.
With CSS, it's awkward to use id values starting with a digit; the simplest thing is to avoid using them.
But with what you have, you can do this:
$(".item[id='1']").css(/*...*/);
Just using #1 as a selector may work in certain select situations provided they don't rely in CSS parsing, but is invalid. If you must have an id value starting with a digit, to use it with a CSS selector, you have to escape it. The escaped version of #1 is #\31, which gets even more awkward when written as a JavaScript string: $("#\\31").css(/*...*/);.
But this works and is in-spec:
$("#\\31").css(/*...*/);
This works in jQuery as of the current version and is not in spec:
$("#1").css(/*...*/);
It could stop working at any time, however, as it's not in-spec, and breaks if you combine it with various things.
I am writing a userscript that grabs an element with something like:
var theElement = $('div.someClass:last');
To grab the last element in the class .someClass so I can parse it.
This is where my question comes in. There is another script on this page dynamically adding a new <div class="someClass"> every once in a while. I want to always have the last element on the page with a class of .someClass selected.
Will Javascript/jQuery always have the latest element or will I have to manually "refresh" it?
Sushanth's answer is correct, but according to this article you could use
var theElements = document.getElementsByClassName('someClass');
and then reliably use
var theElement = $(theElements[theElements.length - 1]); // wrapping in $() is optional
which is worth doing as $(selector) is quite an expensive operation to perform.
edit - only for ie9 and above though http://caniuse.com/getelementsbyclassname
Your selector is evaluated and the result is returned. If you want to do what you're asking, you'll have to re-evaluate that selector.
Nope .. It will not be automatically refreshed.
Every single time you modify something directly on the selector it is a good idea to cache it again.
The selector you wish is not a live Node list.
In such cases if there seem to any changes in the selector I prefer not to cache at all in the first case.
So that I can directly use the selector and not the cached one.
As a example of jQuery code (https://coderwall.com/p/7uchvg), I read that the expression $('#foo a'); behaves like this:
Find every a in the page and then filter a inside #foo.
And it does not look efficient.
Is that correct? And if yes, how should we do that in a better way?
That is correct - Sizzle (jQuery's selector engine) behaves the same way as CSS selectors. CSS and Sizzle selectors are evaluated right-to-left, and so #foo a will find all a nodes, then filter those by nodes that descend from #foo.
You improve this by ensuring that your leaf selectors have a high specificity, usually by giving them a class or ID.
how should we do that in a better way?
Use the context parameter from jQuery.
$('a', '#foo');
Now jQuery will search all anchors within the context of the element with id: foo.
In your query the context is defaulted to document when omitted:
$('#foo a'); == $('#foo a', document);
In this case, your query is indeed not efficient.
You might take a look at this article.
While it is true that Sizzle is a right-to-left engine (which is the same way css is interpreted), it is not true that the specific selector in your example would select all anchor elements on the page and then filter their parents to match the id of "foo". Sizzle actually optimizes any selector that starts with an ID and uses that as the context for the entire selection, rather than using the document. In other words, the selector you've chosen basically translates to:
document.getElementById("foo").getElementsByTagName("a")
Really, that's not a bad selector at all.
However, given the other things jQuery needs to do (which includes looping over the elements to merge them onto the jQuery instance), jQuery("#foo").find("a") will always be the fastest because jQuery implements a jQuery object creation shortcut for id-only selectors, and then it does the find rooted from #foo.
In other words, Sizzle itself is not much different when doing Sizzle("#foo a") and Sizzle("a", document.getElementById("foo")), but jQuery("#foo").find... will be faster because of jQuery's own ID shortcut.
By the way, my remarks on Sizzle is assuming querySelectorAll is not being used. If it is, Sizzle just passes it on to qsa, which still isn't as fast as using jQuery's ID shortcut.
You can use find() for more granular control on your selector order:
$('#foo').find('a');
This will of course be more impressive with more complex selectors, where you can chain find() and filter().
For the record $('#foo').find('a') === $('a','#foo')
[Update] ok, I realized later that it's exactly what your link says...
The jQuery selector engine (Sizzle) has been refactored last year, you'll find detailed explanations here:
http://www.wordsbyf.at/2011/11/23/selectors-selectoring/
Instead of filtering with a inside #foo elements, simply attach a class to a elements and get a elements with class like $("a.class");. This would be more efficient.
Yet another "try it for yourself":
jsperf for various selectors on 10000 elements
jsperf for various selectors on 300 elements
jsperf for various selectors on a "more representative DOM"
Doesn't seem to be much difference with a "flat" DOM (1 & 2), but the performance varies much more with a nested DOM.
Also note that some of the test cases aren't selecting the correct elements (i.e. $('.a') vs $('.a', context)), but I left them from the original tests just for comparison.
This example will retrieve the all anchors elements a in an element called foo, to Find every a in the page and then filter a inside #foo as you want u should select a #foo
$("a #foo");
this will retrieve all the foo elements inside a elements.
Basically I've got two anchor tags separated by a div.
<a class="foo"></a>
<div class="count">0</a>
<a class="bar"></a>
and I'm doing something like this:
var addClass = $(this).parent().children('a:first-child').addClass('new');
It will then add it to the first anchor tag like so:
<a class="foo new"></a>
I know using the nth:child selector is pretty expensive, so I was wondering if there was an easier way to select that previous anchor node, (while ignoring the second one).
You could do this
$(this).parent().children('a:eq(0)').addClass('new');
Learn more about :eq()
Alternatively, if there are no elements between the <a> and the <div>, you could do
$(this).prev('a');
Learn more about .prev()
I'd probably combine prevAll with first (or eq(0), since first just calls eq(0) — but I find first more readable):
$(this).prevAll("a").first().addClass("new");
// or
$(this).prevAll("a").eq(0).addClass("new");
prevAll returns all of the element's preceding siblings, in order starting with the closest sibling, and of course first / eq(0) reduces the set to the first element.
You might be tempted to use the :first selector with prevAll to avoid building up an unnecessary list of siblings, but to my surprise it works better to use first after-the-fact.
How about:
$(this).siblings('a').eq(0).addClass(0);
Actually, if your structure is as simple as in your example, you can just do a simple:
$(this).prev().addClass(0);
jQuery's traversal methods give you lots of ways to get to any given destination.
This question is related to performance.
If I use a selector like the following
$('#myID a') // Does this find #myID and filter by a?
Or should I write the statement like this?
$('#myID').find('a')
I'm not sure if jQuery is smart enough to execute this statement using the ID first or if it operates exactly like CSS and reads right to left. It's not such a big deal using tags but when you run something like
$('#myID .myClass')
It makes a HUGE difference in performance.
From a NetTuts article: http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-think-right-to-left-with-jquery/
As an example, if Sizzle comes across a selector like $('#box p'),
it’s true that it works right-to-left, but there’s also a quick regex
optimization that will first determine whether the first section of
the selector is an id. If so, it’ll use that as the context, when
searching for the paragraph tags.
Relevant comment from SizzleJS:
// Take a shortcut and set the context if the root selector is an ID
// (but not if it'll be faster if the inner selector is an ID)
When an Id is in the selector. jQuery will first execute document.getElementById then begin filtering for child elements.
basically this is why it is never a great idea to use just attribute or class selectors $('.someclass') or $('[name=myname]') without being more specific. Because it causes the code to traverse the DOM and look at every element to find that class.
By just adding a tagname to the same selector $('div.someclass') or $('div.[name=myname]') you improve efficiency becuase it will first run. document.getElementsByTagName narrowing the number of elements to search.