NodeIterator vs Xpath for DOM iterating/finding in Javascript - javascript

Which is better to use when iterating over DOM elements or finding a a set of DOM elements. is Xpath faster?

If you want to find a group of elements that are at different hiearchical levels and different attributes of interest then you should use Xpath.
If you just want to "iterate" one by one over a set of elements then just use the iterator.

Xpath is more clean.
EDIT: Sorry, I don't know if it is faster.

The dojo team did some research about when XPath is faster for their dojo.query interface.
See their Blog Post about it.

according to this post "The DOM traversal code is about four times faster than the XPath code."
http://www.sklar.com/blog/archives/85-Speed-DOM-traversal-vs.-XPath-in-PHP-5.html

Related

Is jQuery traversal preferred over selectors?

Is using $("#vacations").find("li").last() is a better practice than $("#vacations li:last")?
Background and my thoughts:
I was playing with a nice interactive try jQuery tutorial and one of the tasks says:
As you are looking through your code, you notice that someone else is selecting the last vacation with: $("#vacations li:last"). You look at this and you think, "Traversal would make this way faster!" You should act on those thoughts, refactor this code to find the last li within #vacations using traversal instead.
Why would I think so? For me usage of selectors looks a bit higher level than traversing. In my mind when I am specifying a selector it is up to jQuery how to better get the single result I need (without need in returning interim results).
What is that extra overhead of using composite selectors? Is it because current implementation of selectors logic just parses the string and uses the traversal API? Is parsing a string that slow? Is there a chance that a future implementation will use that fact that it does not need to return interim results and will be faster than traversal?
There's no cut and dry answer to this, but with respect to the :last selector you're using, it's a proprietary extension to the Selectors API standard. Because of this, it isn't valid to use with the native .querySelectorAll method.
What Sizzle does is basically try to use your selector with .querySelectorAll, and if it throws an Exception due to an invalid selector, it'll default to a purely JavaScript based DOM selection/filtering.
This means including selectors like :last will cause you to not get the speed boost of DOM selection with native code.
Furthermore, there are optimizations included so that when your selector is very simple, like just an ID or an element name, the native getElementById and getElementsByTagName will be used, which are extremely fast; usually even faster than querySelectorAll.
And since the .last() method just grabs the last item in the collection instead of filtering all the items, which is what Sizzle filters normally do (at least they used to), that also will give a boost.
IMO, keep away from the proprietary stuff. Now that .querySelectorAll is pretty much ubiquitous, there are real advantages to only using standards-compliant selectors. Do any further filtering post DOM selection.
In the case of $("#vacations").find("li"), don't worry about the interim results. This will use getElementById followed by getElementsByTagName, and will be extremely fast.
If you're really super concerned about speed, reduce your usage of jQuery, and use the DOM directly.
You'll currently find notes in the docs for selectors like :last, that warn you about the performance loss:
Because :last is a jQuery extension and not part of the CSS specification, queries using :last cannot take advantage of the performance boost provided by the native DOM querySelectorAll() method. To achieve the best performance when using :last to select elements, first select the elements using a pure CSS selector, then use .filter(":last").
But I'd disagree that .filter(":last") would be a good substitute. Much better would be methods like .last() that will target the element directly instead of filtering the set. I have a feeling that they just want people to keep using their non-standards-compliant selectors. IMO, you're better of just forgetting about them.
Here's a test for your setup: http://jsperf.com/andrey-s-jquery-traversal
Sizzle, jQuery's selector engine, parses the string with regex and tries to speed up very basic selectors by using getElementById and getElementsByTagName. If your selector is anything more complicated than #foo and img, it'll try to use querySelectorAll, which accepts only valid CSS selectors (no :radio, :eq, :checkbox or other jQuery-specific pseudo-selectors).
The selector string is both less readable and slower, so there's really no reason to use it.
By breaking the selector string up into simple chunks that Sizzle can parse quickly (#id and tagname), you're basically just chaining together calls to getElementById and getElementsByTagName, which is about as fast as you can get.

Which is more efficient - $('selector').last() or $('selector:last')?

I have a parent element with a real lot of child elements (1000s). I am looking for the fastest possible way to get a handle to the last child element. The options I've found are:
$('.parent .child').last()
and
$('.parent .child:last')
Any opinions on which one is reliably faster across browsers?
EDIT
I wrote a test in jsfiddle to measure this out and it turns out the difference is pretty much negligible. Though .last() was performing better, the difference is negligible. So i think even with the :last selector, it is actually getting the whole list of elements and then returning the last element? Unbelievable.
Fiddle: http://jsfiddle.net/techfoobar/GFb9f/8/
Many modern browsers support document.querySelectorAll(), so $('.parent .child').last() should be faster, as the selector string can be passed as is, and then the last matched item popped off.
In the latter, the :last is not a standard pseudo selector, and Sizzle has to start chunking the selector string to start matching.
Overall though, I would use what you believe is the most readable. To begin optimising this, first ensure that your application has performance issues and you have identified this selector as the bottleneck.
You have to see this performance test!
UPDATE: There are already good answers on this related question.

Explore tree structure in javascript in a For Loop

What options do I have to access the elements of a DOM tree in a For Loop ? And if it's too difficult can I convert it to an array ?
thanks,
Bruno
Here's an example on jsfiddle.
If you have any questions, don't hesitate to ask. XML has a magnificent traversal system, this doesn't even begin to cut into the raw power of the DOM.
Also, be sure to check w3schools, although it's not a perfectly reliable source.
The DOM tree allows you to navigate down the levels using .children or .childNodes().
.children() provides an array of DOM elements below the current one, and .childNodes() provides all nodes, including text nodes.
You can also use getElementById() to get a specific node (much quicker than any array search could ever be), and getElementsByTagName() to get all elements of a particular type.
I definitely wouldn't recommend converting it to an array -- the DOM tree as it stands is much more flexible than any array.
If you need more flexibility, you could try JQuery, which gives you even more flexibility for searching the DOM by adding complex CSS-style selector queries to the mix. (modern browsers also provide this natively with the getElementsBySelector() method, but this isn't available in all browsers, so you're better off using JQuery or similar for this for the time being)

Get element by tag name shorthand?

I don't know what it's called, but I know that there's a way to get elements based on their tags without getElementsByTagName. It returns the same thing, but it's shorter and I'm using tags a lot in my project. What I'm talking about is document.frames[x] and document.images[x], but with other elements, like document.b[x] or document.a[x]. Seeing as document.images isn't the same as the <img> tag, it seems like if there are more they'd be named differently as well. Would anyone happen to know what it's called when using this method and/or have a list of accepted tags? Thanks.
P.S. Please do not suggest using a library such as jQuery. This project is meant to be a learning experience, so I want to use regular JavaScript.
As mentioned elsewhere in the answers, this doesn't have anything to do with JavaScript really, these are DOM properties and methods accessible via the JavaScript language binding for the DOM.
With reference to addressing elements such as document.frames[x] (note that this is incorrect, it should be window.frames[x]) and document.images[x] - these are Document Object/HTML Collections and the W3C standard includes only images, applets, links, forms and anchors.
So unless I'm looking in completely the wrong place, from what I can tell from the DOM-1 and DOM-2 specs, there doesn't seem to any way of arbitrarily addressing elements by tag name the way that you remember doing.
Update
The MDC entry on HTMLCollection is more understandable; it reads
The following lists each item (and its specific properties) which return an HTMLCollection: Document (images, applets, links, forms, anchors); form (elements); map (areas); table (rows, tBodies); tableSection (rows); row (cells)
Other than other JavaScript libraries creating these shorthands, I am not aware of any that are built into the language. It would be trivial to map this to your own shorthand:
var $ = document.getElementsByTagName;
// you can then use it like so:
$('SPAN').// and so on
Other than this, there is no built-in array-like access to all of the tags in the document:
http://www.javascriptkit.com/jsref/document.shtml
Create your own reference,
document.tag = document.getElementsByTagName;
or a wrapper,
function tag(name) {
return document.getElementsByTagName(name);
}
The only APIs I know of that support querying by element name are,
DOM
getElementsByTagName
CSS Selectors
querySelectorAll
XPath
evaluate
E4X
(mozilla only, and doesn't work with the DOM yet)

jquery: when does $("???") scan the whole DOM?

When using $("#xxx") I guess under the hoods jQuery uses getElementById.
What about $(".xxx") does it scan the whole DOM every time?
jQuery attempts to use the fastest selection method to get what you asked for. There are a number of good resources with performance optimization tips out there that relate directly to jQuery:
Good ways to improve jQuery selector performance?
http://www.artzstudio.com/2009/04/jquery-performance-rules/
http://www.componenthouse.com/article-19
http://www.learningjquery.com/2006/12/quick-tip-optimizing-dom-traversal
See the context argument to the $ function. If not supplied, it defaults to the entire document.
So to answer your question:
$('whatever'); // scans the entire `document`
$('whatever', element); // scans only within element
What about $(".xxx") does it scan the whole DOM every time?
If you don't do the caching: yes. Caching is simple enough:
var $myCachedElements = $('.myElements'); // DOM querying occurs
$myCachedElements.animate({left: '1000px'}, 'slow'); // no DOM Querying this time, as long as you use the variable.
Many browsers do not support getElementsByClassName as a native DOM function, so jQuery has to do the work itself by checking each element's classes.
Here's a compatibility table for document.getElementsByClassName: http://www.quirksmode.org/dom/w3c_core.html#gettingelements
The browsers in green for getElementsByClassName will not require a full DOM scan for $(".className") selectors, and will use browser-native methods instead. The ones in red will be slower.
The difference isn't as pronounced as you'd think though, even for thousands of elements.

Categories