Method for testing DOM selector efficiency - javascript

Is there any straightforward way to evaluate DOM selector efficiency on a page? My curiosity comes from a page written with jQuery, but ideally I'm looking for something more generic (I appreciate the efficiency of selector may differ between approach used to locate it).
This came from a situation where I had a form, say #myForm which contained a <button class="button">
As a selector I was using #myForm button, but I could obviously use #myForm .button , #myForm button.button and a few other alternatives.
Whilst in this example the different is trivial, I wondered whether there is a general consensus as to best approach and further to that, whether there is an easy way to pop together a test page of sorts to experiment with combinations. This then made my realise I have not a clue how to performance test a selector, does anyone have any preferred routes to perform this type of action? Any thoughts appreciated.

The order of dom selectors according to this article:
http://csswizardry.com/2011/09/writing-efficient-css-selectors/
ID, e.g. #header
Class, e.g. .promo
Type, e.g. div
Adjacent sibling, e.g. h2 + p
Child, e.g. li > ul
Descendant, e.g. ul a
Universal, i.e. *
Attribute, e.g. [type="text"]
Pseudo-classes/-elements, e.g. a:hover
I've created a jsperf text with some example selectors to show how you can test each one:
http://jsperf.com/dom-selector-testing
From those results the fastest to the slowest is:
getElementById
getElementsByTagName
getElementsByClassName // ie8 does not support this
querySelector

Related

Using CSS selectors instead of XPath locators when searching through siblings

Currently, I have the following page object fields:
this.filterTeamDropdown = $("filter-item-edit .dropdown button");
this.teams = this.filterTeamDropdown.all(by.xpath("following-sibling::ul//li[contains(#class, 'dropdown-list-item')]"));
Is there a way to replace the XPath locator for the teams field and have a CSS selector instead?
The motivation for it is coming from the Style Guide and the recommendation not to use XPaths.
From what I understand, it is impossible to have a CSS selector to go to the next sibling starting from current element in the context. But, are there any alternatives?
Saying NEVER to anything is silly. I strongly favor CSS selectors because locating element by id, CSS selector, just about anything... is faster than XPath. But... at the same time we're talking a few ms of difference.
There are some things that XPath can do that no other locator method can. One example that comes to mind is finding an element (other than A) by contained element text. Other than that I generally stick to CSS selectors when ID doesn't work.
I strongly dislike a lot of people's locator strategies on SO because XPath seems to be the goto way to find elements to the point where it's silly. I've seen people looking for nothing but an id and using XPath. I think part of it is the ease of which you can obtain an XPath, right click on element in inspector and copy XPath and paste in code. The problem with that, as I'm sure you know, is that sometimes (many times?) that results in a very brittle XPath but some/many people don't know any better.
All that said, I'll point you to the W3C CSS Selector reference and maybe you can find what you are looking for. There are some sibling combinators in there but I don't have your HTML so I don't know which, if any, of them would work.
https://www.w3.org/TR/selectors/#selectors
https://www.w3.org/TR/selectors/#adjacent-sibling-combinators
I just read some of the comments below your question and see that you already knew about the + combinator. Is there some reason you can't reuse your initial locator CSS with the XPath converted string? I don't know if this is even valid/usable but I've combined the two locators you provided in your code after converting the XPath to a CSS selector.
filter-item-edit .dropdown button + ul li.dropdown-list-item
There is an another, Protractor-specific way to solve it - use the locator() of the parent element and concatenate to make a child element selector:
this.filterTeamDropdown = $("filter-item-edit .dropdown button");
this.teams = this.filterTeamDropdown.$$(this.filterTeamDropdown.locator().value + " + ul li.dropdown-list-item")

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()

jQuery selector for finding the first element after a given element that matches a selector

Let's say I have this HTML:
<textarea>blah</textarea>
<br>
<div class="select_this">hello!</div>
How can I select the DIV with class "select_this" when I already have the textarea identified? I don't want to use a class selector on the entire document because I'm working with a large document and class lookups are slow in older browsers.
jQuery .next() doesn't seem to do the trick, closest() only looks up the DOM tree, and .nextUntil() qualifies on everything I need except for the "select_this" div. Any other options out there?
There are two ways to do it.
Use this if you only have a few siblings:
$('textarea').nextAll('div.select_this').first();
The downside of this is that it test every subsequent element to see if it matches the selector, even after it's found the first one. Use this if you have many, many siblings, to save on evaluation:
$('textarea').nextUntil('div.select_this').andSelf().last().next();
Note also that it's better to use the first and last methods, rather than their corresponding selectors (:first, :last), because browsers don't natively understand the selectors, which slows the expression down considerably.
Edited to incorporate andSelf per comment below.
You want nextAll:
jQuery(yourTextarea).nextAll('.select_this:first');

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

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