In jQuery, can I use functions instead of CSS selectors? - javascript

Rather than use $('.list').find('li:not(.done):last'), which returns all LI that is not .done and is the last of every .list, how can I use functions instead?
For instance, $('.list').find('li').not('.done').last() would return just one item, because it searches all .list for LI that aren't .done and then just returns the last one.
There are a few reasons I want to do this. Primarily, typically using functions are faster than CSS selectors (in some cases, especially when you split them up into multiple functions as jQuery doesn't have to manually split up the selectors, and oftentimes jQuery functions are just mapped directly to existing CSS selectors anyway). But anyway, curiosity is my main reason at the moment, and performance is secondary.
Sample code (not entirely representative of the actual code, but the structure is similar):
<ul class="list">
<li>One</li>
<li class="done">Two</li>
</ul>
<ul class="list">
<li class="done">Three</li>
<li>Four</li>
</ul>
I want to return the nodes for:
One and Four.

I'm not coming up with a way to do it that doesn't require looping, e.g.:
$(".list").map(function() {
var items = $(this).find("li").not(".done");
return items[items.length - 1];
});
// Old way
$(".list").find("li:not(.done):last").css("color", "red");
// Just to show it's wrong (as the question says)
$(".list").find("li").not(".done").last().css("background-color", "yellow");
// Alternative
$(".list").map(function() {
var items = $(this).find("li").not(".done");
return items[items.length - 1];
}).css("border", "1px solid black");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul class="list">
<li>One</li>
<li>Two</li>
<li class="done">Three (done)</li>
</ul>
<ul class="list">
<li>One</li>
<li>Two</li>
<li class="done">Three (done)</li>
</ul>
The reason you might think using selectors is slower is that selectors like :last are jQuery extensions, not true CSS selectors, meaning that Sizzle (jQuery's selector engine) has to process them in JavaScript, rather than offloading them to the (much faster) selector engine built into the browser. If you restrict yourself to CSS selectors the browser implements, typically the selector will win (modulo other factors, and YMMV). I can't account for your seeming to see the DOM being changed by :not(.done) (which is a valid CSS selector), unless Sizzle does something It Really Shouldn't to process that (since Sizzle supports complex selectors within :not whereas CSS doesn't).

You don't actually need a function. Any of the following selectors would return the nodes for One and Four:
$('.list li:not(.done)')
$('.list li').not('.done')
$('li:not(.done)')
Using $('.list li:last-child').not('.done') would return those items that are both the last child and don't have a done class (Four).
That said, if you really want to use a function, you could go with jQuery's .contents() and .filter() methods.
$('.list').contents().filter( function(i,v){
if( v.nodeType === 1 && !$(v).hasClass('done') ){
return v; // Returns 'One' and 'Four'
}
});
Here's a fiddle of all of the above.

There are a few reasons I want to do this. Primarily, typically using functions are faster than CSS selectors
There are LIs that I don't want, and I want the last of every list that doesn't match a class
The fastest method would be XPath, since it is native:
//*[#class='list']/li[#class!='done']/*[last()]
But anyway, curiosity is my main reason at the moment, and performance is secondary.
jQuery 1.0 supported XPath, then switched to the selector engine which eventually became Sizzle:
Lately, I’ve been doing a lot of work building a parser for both XPath and CSS 3 – and I was amazed at just how similar they are, in some respects – but wholly different in others. For example, CSS is completely tuned to be used with HTML, with the use of #id (to get something by ID) and .class (to get something by its class). On the other hand, XPath has to ability to traverse back up the DOM tree with .. and test for existence with foo[bar] (foo has a bar element child). The biggest thing to realize is that CSS Selectors are, typically, very short – but woefully underpowered, when compared to XPath.
which were removed in 1.2:
Since XPath selectors were removed form jQuery in 1.2, a new XPath Selector Plugin has been introduced. You can use this plugin to give yourself the CSS/XPath hybrid selectors that have existed in jQuery since its creation.
DOM traversal techniques include:
Tree Walker
An implementation using the DOM Level 2 Tree Walker methods. Builds a generic filter function and traverses through all elements.
XPath
Building a single expression and letting the XPath engine traverse through the document, finding all the relevant elements.
Hybrid
Mixes an XPath and DOM implementation; using XPath wherever possible.
References
John Resig - XPath and CSS Selectors
John Resig - getElementsByClassName Speed Comparison
jQuery 1.2 Release Notes
jQuery XPath Plugin

Related

jquery select all children after nth-child

I am working with jquery and creating show hide lists, I need hide all the child list items that follow the 6th child in the ul. Normally I would do this by setting the height and then changing the height on click of a click, but for this to work I need to add overflow:hidden to my css, and this is not an option.
How would I get all the child list elements that are greater than the 7th child?
Something like,
$("ul li:6n").domeSomething()
How would I get all the child list elements that are greater than the 7th child?
Select the element index = 7+
$("ul li:gt(6)").domeSomething()
:gt selector
The selector uses the zero base index:
Note that since JavaScript arrays use 0-based indexing, these selectors reflect that fact. This is why $('.myclass:gt(1)') selects elements after the second element in the document with the class myclass, rather than after the first. In contrast, :nth-child(n) uses 1-based indexing to conform to the CSS specification.
I wanted to write my answer only to discuss two previous answers: the answer from gdoron and the answer from neu-rah. If we would see on voting count one can see that the most reader find doron's answer better. I disagree and I try to explanation why.
The explanation you would find the documentation of :gt() Selector (see "Additional Notes:" here):
Because :gt() is a jQuery extension and not part of the CSS
specification, queries using :gt() cannot take advantage of the
performance boost provided by the native DOM querySelectorAll()
method. For better performance in modern browsers, use
$("your-pure-css-selector").slice(index) instead.
You can test the code with here or better here (with non-minimized code of jQuery 1.7.2). The code use just the statement $("ul li:gt(6)").css("color", "red");. You will better understand the problem if you start the demo in Developer Tools of Chrome with activated button "Pause on exceptions". You will see the following exception (it will be not the first one):
So you can see that the current implementation of jQuery try to use native querySelectorAll() of the web browser to get the best performance. After that the function $.makeArray will be used to make jQuery wrapper from the NodeList. In case of exception the line
return oldSizzle(query, context, extra, seed);
So you will have the best performance if you would use selectors supported by querySelectorAll(). For example
$("ul li").slice(7)
is better as
$("ul li:gt(6)")
I would you recommend to use more exact selectors whenever it's possible. For example if the parent <ul> element has id="menu1" then you can use
$("#menu1 >li").slice(7)
for the best results. It will help additionally in case of multiple <ul> elements on your page.
Someone can mention: the selector "ul li:gt(6)" works quickly enough. It's correct, but you should don't forget, that you use sometime selectors inside of loop or use it inside of functions which will be called inside of loops. So the difference between 10ms and 30ms can be much more clear if the execution time will be multiplicate to 100.
Moreover it somebody ask yourself the question how to implement some selection and get the answer, he/she will use the code pattern permanently. I think that it would be better to use the pattern which has performance advantage. Isn't so.
UPDATED: To clear the difference in performance of $("ul li:gt(6)"), $("ul li").slice(7) and $("#menu1 >li").slice(7) selectors I made additionally the demo. Everybody can test the difference in the web browser which he uses. You should additionally not forget, that in case of page having many other elements the id selector will work better.
On my computer the execution time of $("ul li").slice(7) and $("#menu1 >li").slice(7) are about the same (the page have very little elements) and is about 2.5-4.5 time better as the for $("ul li:gt(6)"). The results can depend on the number of li items, the number of elements on the page and many other things, but I hope that the test do clear shown that the usage of slice has performance advantage compared with the usage of :gt (exactly like we can read in the jqGrid documentation referenced before).
use slice to reduce a set
.slice(start[,end])
http://api.jquery.com/slice/
example (edited)
$("ul li").slice(6).doSomething(...)
Try this:
$('ul li:eq(6)').nextAll().domeSomething()

When removing a class with jQuery, is it better to include the class in the selector?

Probably this will not result in any noticable performance difference, but I was just interested. If I am removing a class on a list of elements using jQuery, is it better practice or performance to include the class in the selector?
So either include it:
$('#myList li.classToRemove').removeClass('classToRemove');
or don't include it:
$('#myList li').removeClass('classToRemove');
So basically is there any difference to narrowing the list in the selector and in the removeClass method.
Selectors are matched backwards (last to first), meaning it will first find "all elements with "classToRemove" which are also "li" which are also children of "#mylist" ".
It's done that way to narrow down the list as much as possible, as soon as possible.
Therefore, you should go with
$('#myList li.classToRemove').removeClass('classToRemove');
Less items to match means a faster, more efficient script.
Assuming that not all the <li> elements have that class, then I'd say that it's better to run .removeClass() on the filtered set.
But overall, there will be other things that will have an impact on the performance, like...
The number of elements matched by '#myList li' vs '#myList li.classToRemove'
The way different environments optimize their DOM selection
The actual performance difference of the innards of the .removeClass() method between different environments
So unless you're talking about unnecessarily running .removeClass() on a large subset of elements, I wouldn't worry about it too much.
Using the more specific selector will improve performance but it won't affect the result of .removeClass().
Think of each addition to a selector like a drill down. Given the html below:
<div id="myList">
<ul>
<li id="li1"></li>
<li id="li2"></li>
<li id="li3" class="classToRemove"></li>
</ul>
</div>
//In order of fastest to slowest:
//Selecting the exact li and calling removeClass once
$('#li3').removeClass('classToRemove');
//In this one we are skipping 2 li's and then calling removeClass once
$('#myList li.classToRemove').removeClass('classToRemove');
//In this one we have to call removeClass 3 times
$('#myList li').removeClass('classToRemove');
Sorry to bump an old post.
The test at this page seems to suggest that NOT including the class selector is around 10-15% quicker. So, if you want to use jQuery, the best approach is just:
$('#myList li').removeClass('classToRemove');
Note - this page does appear to use jQuery 1...

jQuery selector speed

Is selecting by ID-only (or a single identifier) faster than when adding additional identifiers?
For example
$('#element')
vs
$('#container #element')
or even more specific:
$('body div#container div#element')
?
Yes, selecting by ID only should be the fastest, because jQuery (or Sizzle) can directly use getElementById. Otherwise the rest of the selector has to be evaluated as well, right to left.
$('#element') should be fastest, followed by $('div'). These map to the native functions document.getElementById and document.getElementsByTagName. Anything more complex has to go through complex scripting and document searching.
On everything but IE6, 7, and 8, $('.class') also map to the new document.getElementsByClassName function as well, but this is slower than the other two and still has to go through Sizzle if the browser doesn't support it.
You should always try to use as few selectors as you can that will correctly identify the element(s) that you wish to process. For instance if element is a unique id (as it should be), then #element will uniquely specify it and anything else will be wasteful, both in parsing and processing overhead. (This goes equally for CSS style rules, of course, although in this case the optimal choice of selectors may be different than when using jQuery.)
Actually calling
document.getElementById("element");
is fastest.
If you really want a jQuery object then call $(document.getElementById("element"));
Benchmark

How to optimize $.find().first()?

I need to retrieve the first element.
I do that with this code...
$(element).find('.x').first();
As much as I understand, that code...
Retrieves all elements from element that matched .x,
Removes unneeded elements;
Is there any better way to do it? Like $.findOne() or something?
As per jQuery docs:
Because :first is a jQuery extension and not part of the CSS
specification, queries using :first cannot take advantage of the
performance boost provided by the native DOM querySelectorAll()
method. To achieve the best performance when using :first to select
elements, first select the elements using a pure CSS selector, then
use .filter(":first").
So rewriting your selector to:
$(element).find('.x').filter(":first")
or (this one will give you direct descendants only and will be faster than .find, unless you're looking for nested elements too)
$(element).children('.x').filter(":first")
should give you better results.
Update After valuable inputs from kingjiv and patrick dw (see comments),
it does seem that these two are faster than .filter(':first') contrary to what the doc claims.
$(element).find('.x').first(); // faster
$($(element).find('.x')[0]); // fastest
If you want to have it real fast, you should use native browsers methods. Modern browsers support querySelector [docs]:
var $result;
if(element.querySelector) {
$result = $(element.querySelector('.x'));
}
else {
$result = $(element).find('.x').first();
}
The usage is a bit limited, as it would only work if element is a single element and if the selector is a valid CSS selector. You could make a plugin out of it. But then, if you consider all cases, like multiple elements etc., there is probably no advantage anymore.
So again, if you have a very specific use case, this might be useful, if not, stick with jQuery.
Update: Turns out, making a plugin is still faster: jsPerf benchmark
(function($) {
$.fn.findOne = function(selector) {
try {
var element, i = 0, l = this.length;
while(i < l && (element = this[i].querySelector(selector)) === null) {
i++;
}
return $(element);
}
catch(e) {
return this.find(selector).first();
}
};
}(jQuery));
How this works:
The plugin iterates over the selected DOM elements and calls querySelector on each of them. Once an element is found, the loop will terminate and return the found element. There are two reasons an exception could occur:
The browsers does not support querySelector
The selector is not a pure CSS selector
In both cases the plugin will fall back to use the normal jQuery method.
As crazy as it seems, in every performance test I've seen, .first() has better performance than :first.
As most people are suggesting, it seems as though using $(element).find(".x:first") should have better performance. However, in reality .first is faster. I have not looked into the internals of jquery to figure out why.
http://jsperf.com/jquery-select-first
And apparently using [0] and then rewrapping in a jquery object is the fastest:
$($(element).find(".x")[0])
EDIT: See mrchief's answer for an explanation of why. Apparently they have now added it to the documentation.
This should be better
$(element).find('.x:first');
Use :first selector:
$(element).find('.x:first')
It's better to write:
$('a:first');
What you're writing is "in 'element', find '.x' and return the first one". And that can be expressed like this
$('.x:first', element);
how about using first-child pseudo class ? like
$(element).find('.x:first-child')
However it might generate issues if your structure is like
<div>
<p></p>
</div>
<div>
<p></p>
</div>
so actually it is not what you are looking for (if you mean general solution). Others mnetions :first and this seems to be the correct approach
Your bottleneck is really the .find(), which searches all the descendants instead of just the immediate children.
On top of that, you're searching for a class .x (which uses a jQuery custom search) instead of an ID or a tagname (which use native DOM methods).
I would use Mrchief's answer and then, if possible, fix those two bottlenecks to speed up your selector.
You could combine the $(element) and .find() calls using a descendant selector; I'm unsure of the performance comparison:
$("#element .x").first().hide();
That way is fine according to the jQuery documentation, or at least better than using :first selector.
You can try as alternatives .filter(":first") or get the first element using array accessor against the .find() result [0].
Also, instead of .find() you can change it to:
$('.x', element)
To narrow the search to .x elements inside element, intead of searching the whole document.

Fastest selector method in jquery and CSS - ID or not?

What is fastest in jquery/javascript?
$('#myID .myClass')
or
$('.myClass')
What is best to use in CSS?
#myID .myClass{}
or
.myClass{}
I see now that I should have explained better. Sorry!
Ofceauce ID is a faster selector in both CSS and JavaScript. But some times you need to use class since there are multiple selectors.
Say forexample that I have i BIG html document. In the middle of the page I have:
<div id="myID">
<a class="myClass">link1</a>
<a class="myClass">link1</a>
<a class="myClass">link1</a>
</div>
If I want to target all "myClass". Would it then be better to target the ID before targeting the classes? (then I wouldn't have to do domtravel of the entire HTML document) Eg.:
Would this:
$('#myID').find('.myClass')
Be faster than:
$('.myClass')
My testing on modern browsers suggests that you should go with either,
$('#id').find('.class') // or
$('.class')
but not,
$('#id .class')
Reason being that all modern browsers implement getElementsByClassName resulting in almost-constant time lookups by class name (assuming a hash implementation). Which browsers are modern is another subjective question.
They're roughly the same in most modern browsers since class-names are hashed internally. The difference is that older browsers don't have a .getElementsByClassName or equivalent method, so .myClass is parsed internally to jQuery and EVERY element in the dom is walked and checked for the classname (or it uses XPath when possible).
Always try to use #myID .myClass when possible as it allows jQuery to jump directly to #myID and traverse from there when necessary.
Let's just think logically about this for a second, and pretend that you didn't know anything about how a browser is built internally or how it accesses the DOM, but you assume that whatever it does is logical.
Therefore, doesn't it stand to reason that out of two selectors, the narrowest one would find you results faster?
You have two selectors, which translate to rough english as
Any element of the class myClass that is a child of the element with ID of myID
Any element of the class myClass
As for "What is best to use in CSS", this is completely subjective as it depends if you are intending to target all instances of .myClass or just those that are children of #myID.
Good question actually.
Say you have parsed DOM of N elements of max depth of D and CSS of S number of rules. Then the task of finding styles for all elements has computational complexity of roughly O(N*D*S).
Obviously not all of CSS selectors has the same computation complexity.
For example li.item selector and li[class ~= "item"] require exactly the same CPU resources as they are equivalents. li[class = "item"] can be computed faster as it does not require scan of white spaces.
#1 selector here:
#myID .myClass{} /* #1 */
.myClass{} /* #2 */
require more CPU resources as you need to do exactly the same amount of work as in case #2 plus you will need to scan parent/child chain (of max D elements) to find the element with "myID".
That is all about pure CSS selectors.
In jQuery & friends situation can be a bit different. Theoretically jQuery engine can use document.getElementById() to minimize the lookup set (so reduce the N number) but that will not match CSS behavior. Here is an example: http://jsfiddle.net/dnsUF/ . Here jQuery reports one element with #foo but there two such elements in fact.
Resume:
In CSS case #2 is faster
In jQuery case #1 can be faster (but technically may not be correct in CSS sense).
Here is my article about CSS selector complexity:
http://www.terrainformatica.com/2008/07/csss-and-computational-complexity-of-selectors/
And this one of how to improve it by using style sets:
http://www.terrainformatica.com/2010/09/style-sets-in-h-smile-core/
IDs will always be the fastest way to access an element, since they are unique.
Yeah, id is one of the fastest method to access element. Check it out this test http://mootools.net/slickspeed/.
#myID .myClass is definitely a better way to access the element assuming you have many elements to which the .myClass is applied.
Update - 2015 - Check yourself here
https://jsperf.com/id-vs-class-vs-tag-selectors/2
TL;DR;
Using ID $("#foo") is almost 4x faster than CSS $(".bar") on my chrome 41 on linux 64bits

Categories

Resources