jQuery filter efficiency - javascript

I recently came across this question: How do I check if an element is hidden in jQuery?
The currently accepted answer seems to indicate that $('element').is(':hidden') is preferable to $('element:hidden') because the :hidden filter is only being applied to a single element.
But... Calling .is() also adds extra overhead as you are calling an another function.
This got me thinking, does the above reasoning for using .is() hold true if the selector is a set of elements?
And what about more extreme cases? Take the following test case:
$('element.class1:not(.class2):visible[rel="foo"]')
Is it best to leave those in the selector? Or move them all into a single filter call:
$('element').filter('.class1:not(.class2):visible[rel="foo"]')
Or is it better to chain them?
$('element').is('.class1').not('.class2').is(':visible').filter('[rel="foo"]')

The currently accepted answer seems to indicate that $('element').is(':hidden') is preferable to $('element:hidden') because the :hidden filter is only being applied to a single element.
I don't read it that way at all, and assuming you're dealing with a string, then $("some-selector:hidden") isn't applied only to a single element; it's applied to all elements matching some-selector.
Or is it better to chain them?
$('element').is('.class1').not('.class2').is(':visible').filter('[rel="foo"]')
That will fail, since is returns a boolean:
.is(selector)
Description: Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments.
If you want to select elements matching multiple criteria, it's fine to put those criteria in the selector string. The only real question you should ask yourself is whether to use :hidden and other jQuery-isms in the string, since it means that jQuery can't pass the selection off to the browser's built-in (read: fast) selector engine, and has to do the work itself.
For that reason, with that example, you might prefer to move the jQuery-isms out of it:
$('element.class1:not(.class2)[rel="foo"]').filter(":visible")

Related

What does $($(this)) mean?

I saw some code around the web that uses the following statement
if ($($(this)).hasClass("footer_default")) {
$('#abc')
.appendTo($(this))
.toolbar({position: "fixed"});
}
What is the use of $($(this)) and why is that necessary here?
Yes, $($(this)) is the same as $(this), the jQuery() or $() function is wonderfully idempotent. There is no reason for that particular construction (double wrapping of this), however, something I use as a shortcut for grabbing the first element only from a group, which involves similar double wrapping, is
$($('selector')[0])
Which amounts to, grab every element that matches selector, (which returns a jQuery object), then use [0] to grab the first one on the list (which returns a DOM object), then wrap it in $() again to turn it back into a jQuery object, which this time only contains a single element instead of a collection. It is roughly equivalent to
document.querySelectorAll('selector')[0];, which is pretty much
document.querySelector('selector');
You can wrap $ as many times as you want, it won't change anything.
If foo is a DOM element, $(foo) will return the corresponding jQuery object.
If foo is a jQuery object, $(foo) will return the same object.
That's why $($(this)) will return exactly the same as $(this).
There is no specific need for double-wrapping and $($(this)) is exactly the same as $(this).
That said, I once found this double-wrapping in one file in my project, committed by another developer. Tracking the changes through revision, turned out that it started as $($(this).find('selector').first()) - that is, the result of some selector was wrapped to create a new object. Then for whatever reasons, the selector was removed and only the double-wrapping of this remained. Needless to say, on the next commit it was changed to $(this).
As explained before me, $($(this)) and $(this) are absolutely identical. jQuery returns the same jQuery object if you try to wrap it more than once.
Additionally, for performance considerations it is a good practice to reuse jQuery objects - it is quite expensive to create jQuery objects, especially the ones with complex selectors. Example:
var $this = $(this);
if ($this.hasClass("footer_default")) {
$('#abc')
.appendTo($this)
.toolbar({position: "fixed"});
}
Just google for 'jQuery best practices' - it will take a 30 min for you to learn these basics and you will use jQuery way more effectively.
There is no meainig of doing that.
The following code return the same:
console.log($($(this)).hasClass("footer_default"))
console.log($(this).hasClass("footer_default"))
a boolean value depenging on if the selected element has or not the class footer_default:
.hasClass( className )Returns: Boolean
Demo: http://jsfiddle.net/IrvinDominin/aSzFn/
$(this) and $($(this)) both return jquery object.
There is no difference between these two.

JQuery: What's the difference between referencing an element using #[objectId] or [id=objectId]

Can anybody tell me what's the difference between referencing an element using #[objectId] or [id=objectId]?
The first one is very fast, as jQuery internally uses getElementById when it recognizes the pattern (using a regular expression).
The second one asks jQuery to iterate over all objects having an id. It's very slow. jQuery doesn't even stop iterating when it find one match in that case.
The only legitimate reason to use a [id... selector is when you don't just search by an exact id, for example you might want to search all elements whose id starts with "something" using $('[id^=something]').
Assuming you have a valid HTML (no reused id) and a valid id, you can still have problems with $('#'+someId) (for example when your id contains a quote, or anything that breaks Sizzle's pattern recognition system). In that case, use $(document.getElementById(someId)).
Following your comment : Yes, a "#" in an ID makes it impossible for Sizzle (jQuery's selector engine) to understand your selector. Sizzle uses the following regex :
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
and /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/.test('#som#thing') returns false.

Why does the order of the selectors in a queryselector call matter?

I think i may have found a bug in the querySelector function. It seems that the order the selectors are placed affects whether or not the selectors are selected.
I'm trying to select a link using querySelector, and am passing in 3 different selectors, and would like it to select any one of those 3, unfortunately, sometimes it won't match the selector even when there is a definite match on the page, unless I rearrange the order of the selectors in the querySelector function.
Test link that i want to match:
<span>#Coyotes</span> 60 recent posts
This function returns null:
document.querySelector("a[href$='?source=fttp']","a[href$='?source=ftp']","a[href^='/topic/']")
This function call matches the link
document.querySelector("a[href$='?source=ftp']","a[href^='/topic/']","a[href$='?source=fttp']")
both calls have the exact same selectors, only in a different order
I've created a jsfiddle that demonstrates the problem:
http://jsfiddle.net/eB8nv/5/
Can anyone tell me what is going on here, and why the order of the selectors sent to the querySelector function matters?
querySelector() only takes one parameter.
You can't do that.
Instead, you need to pass a single string with three comma-separated selectors.

Performance of jQuery Selectors with ID

I know in jQuery if we use ID to select elements,it's so efficient.I have a question about this selectors:
please consider this 3 selectors:
$('#MyElement')
$('#Mytbl #MyElement')
$('#Mytbl .MyClass')
which one is faster and why?How I can check the time elapsed to select my element in jQuery?
A direct ID selector will always be the fastest.
I've created a simple test case based on your question...
http://jsperf.com/selector-test-id-id-id-id-class
Selecting nested ID's is just wrong, because if an ID is unique (which it should be), then it doesn't matter if it's nested or not.
this is the way to stop times between some javascript calls
selectorTimes = [];
var start = new Date().getTime();
$('#MyElement')
selectorTimes.push(new Date().getTime()-start);
start = new Date().getTime()
$('#Mytbl #MyElement')
selectorTimes.push(new Date().getTime()-start);
start = new Date().getTime()
$('#Mytbl .MyClass')
selectorTimes.push(new Date().getTime()-start);
console.log(selectorTimes);
i think the second selector is not efficient, if you have a domid select this directly:
$('#MyElement')
The first one is the fastest, simply because it only has 1 property to look for. However,
document.getElementById("MyElement")
is even faster, though. It's native javascript, and unlike jQuery, the browser immediately knows what you want to do, instead of having to run through a load of jQuery code to figure out what it is you're looking for, in the first place.
You can use jsPerf to run a speed test, to compare the functions: Test Case. Results:
$('#MyElement')
Ops/sec: 967,509
92% slower
$('#Mytbl #MyElement')
Ops/sec: 83,837
99% slower
$('#Mytbl .MyClass')
Ops/sec: 49,413
100% slower
document.getElementById("MyElement")
Ops/sec: 10,857,632
fastest
Like expected, the native getter is the fastest, followed by the jQuery getter with only 1 selector at less than 10% of the native speed. The jQuery getters with 2 parameters don't even get close to the operations per second of native code, especially the class selector, since classes are usually applied to multiple elements, compared to ID's. (Native ID selectors stop searching after they've found one element, I'm not sure if jQuery does, too.)
A couple things:
More selectors = slower search. If you can get your desired result with fewer predicates, do so.
Getting an element by ID is quicker than getting by a class. getElementById is a core function of JavaScript that is heavily optimised because it is used so frequently. Leverage this when possible.
The space selector (' ') is much more costly than the child selector ('>'). If you can use the child selector, do so.
These rules apply to CSS as they do JavaScript and jQuery.
Here you are. See the comments on top of each example:
//fastest because it is just one id lookup: document.getElementById("MyElement") with no further processing.
$('#MyElement')
//a little slower. JQuery interprets selectors from right to left so it first looks up for #MyElement and then checks if it is hosted in #Mytbl
$('#Mytbl #MyElement')
//the slowest of all. JQuery interprets selectors from right to left so it first finds all elements with .MyClass as their class and then searches for those hosted in #Mytbl.
$('#Mytbl .MyClass')
If you can, always use just the id (like the first example) but if you have to have multiple selectors and classes chained together, try to put the most strict one at the right. For example an id. Because JQuery interprets selectors from right to left.
Also, if you need nested selectors, it's faster to use $().find().find()
http://jsperf.com/selector-test-id-id-id-id-class/2
$('#Mytbl .MyClass')
$('#Mytbl').find('.MyClass')
The latter is about 65% faster.
Obviously the first one, $("#MyElement") is faster than other 2.
Accessing an element with its id always faster but sometimes we had to find some element in some container. In that case we prefer .find() or .filter() (depending upon situation).
The difference between selectors depends on browser to browser. e.g. if you access through class on IE it would be slower than FF. FF is faster when accessing an element with class rather than ID.
In your second example, i.e. $("#mytbl #MyElement"), here you are finding #MyElement under #mytbl which is legal but not appropriate way. Since you already know the ID of the element (assuming you have only one element with this id on your page) so its better to use $("#MyElement"). $("#mytbl #MyElement") will read #mytbl first and traverse to find #MyElement under it hence time consuming and slow.
To test different cases you can write small snippet to read/access atleast 10000 elements in a loop otherwise it would be hard to determine which way is faster.
The fastest will be:
$('#Mytbl', '#MytblContainer' );
because in this case, jquery don't have to search the whole dom tree to find '#Mytbl'. It will search in the scope given only. IE it will serach in '#MytblContainer' only .
I would say that the first one is the fastest because you're simply searching for one id.
And
$('#Mytbl .MyClass')
is the slowest because you're not specifying the type of element that has the class "MyClass"

Filtering elements out of a jQuery selector

I have a page that selects all the elements in a form and serializes them like this:
var filter = 'form :not([name^=ww],[id$=IDF] *,.tools *)';
var serialized = $(filter).serialize();
This works, unless the form gets around 600+ elements. Then the user gets s javascript error saying that the script is running slow and may make their browsers unresponsive. It then gives them the option to stop running the script.
I have tried running the filters separately, I have tried using .not on the selectors, then serializing them, but I run into one of two problems. Either it runs faster without the error, but also does not filter the elements, or it does filter the elements and gives me the slow script error.
Any ideas?
With 600+ elements this is going to be dead slow. You need to offer Sizzle (jQuery's selector engine) some opportunities for optimisation.
First, consider the fact that jQuery can use the natively-supported querySelectorAll method (in modern browsers) if your selector complies with the CSS3 spec (or at least to the extent of what's currently supported in browsers).
With your case, that would mean passing only one simple selector to :not instead of 3 (1 simple, 2 complex).
form :not([name^=ww])
That would be quite fast... although you're not being kind to browsers that don't support querySelectorAll.
Look at your selector and think about how much Sizzle has to do with each element. First it needs to get ALL elements within the page (you're not pre-qualifying the :not selector with a tag/class/id). Then, on each element it does the following:
(assume that it exits if a result of a check is false)
Check that the parent has an ancestor with the nodeName.toLowerCase() of form.
Check that it does not have a name attribute starting with ww (basic indexOf operation).
Check that it does not have an ancestor with an id attribute ending in IDF. (expensive operation)
Check that it does not have an ancestor with a class attribute containing tools.
The last two operations are slow.
It may be best to manually construct a filter function, like so:
var jq = $([1]);
$('form :input').filter(function(){
// Re-order conditions so that
// most likely to fail is at the top!
jq[0] = this; // faster than constructing a new jQ obj
return (
!jq.closest('[id$=IDF]')[0]
// this can be improved. Maybe pre-qualify
// attribute selector with a tag name
&& !jq.closest('.tools')[0]
&& this.name.indexOf('ww') !== 0
);
});
Note: that function is untested. Hopefully you get the idea...
Could you maybe just serialize the whole form and do your filtering on the backend? Also, why-oh-why is the form growing to 600+ fields?
use the :input selector to only select applicable elements..

Categories

Resources