I have following structure
<ul id="tabs" class="nav nav-tabs">
<li>AAA</li>
<li>BBB</li>
<li>CCC</li>
<li>DDD</li>
</ul>
Now I am operating on the anchor tag by following code and which is working fine.
$('#tabs a[href="#ddd"]').tab('show');
I am using pycharm which adds warning for the line by saying "Preface with ID selector". When I click it, pycharm changes to following
$('#tabs').find('a[href="#ddd"]').tab('show');
Both are working fine but I don't understand the difference.
What is the difference in both or more specifically what is difference between $('#tabs a[href="#ddd"]') and $('#tabs').find('a[href="#ddd"]')?
$("#tabs a") evaluates from right to left - which is the native direction of both Sizzle selector engine and querySelectorAll - i.e. first it finds all of the anchor elements in the page and then narrows it down to those under #tabs.
$("#tabs").find("a") evaluates - more intuitively - from left to right, i.e. first it finds #tabs, and then only the anchor elements under it.
Clearly the latter would yield better performance, but it would only be noticeable accumulatively; that is, if you run thousands of queries. Otherwise, the difference is negligible.
As stated in "Increase Specificity from Left to Right":
A little knowledge of jQuery’s selector engine is useful. It works
from the last selector first so, in older browsers, a query such as:
$("p#intro em");
loads every em element into an array. It then works up the parents of
each node and rejects those where p#intro cannot be found. The query
will be particularly inefficient if you have hundreds of em tags on
the page.
Depending on your document, the query can be optimized by retrieving
the best-qualified selector first. It can then be used as a starting
point for child selectors, e.g.
$("em", $("p#intro")); // or
$("p#intro").find("em");
But Test case says $("#tabs > a") would be fastest
The second one is MUCH quicker.
The reason being jQuery's selector enginge Sizzle, which traverses the selection from right to left, not vice versa.
This means that the selector
$('#tabs a[href="#ddd"]')
First queries the DOM document for a tag, which contains the attribute href set to #ddd. It then filteres out all of them, to get every one that is a <a> tag. At last, it traverses up the DOM tree for every node, trying to find a parent #tabs.
Imagine a site with 1.000 tags with href="#ddd", how tremendously slow that would be.
THEN.
The other variation pycharm suggest, is to first locate a element #tabs. This is super-quick, since jQuery can utilize the native browser method getElementById(). Having this node, it can traverse down to find all tags that are matching. By doing this, not all tags in the whole DOM-tree, needs to be checked. Only those which actually are in #tabs.
For further information, please check out this page in the documentation.
The effect is the same: Find anchors that have the value #ddd as href and are a descendant of #tabs. The difference lies in the way to achieve this.
The first solution finds the anchors and then checks if they are a descendant of #tabs.
The second solution finds #tabs and then finds the anchors. Which should be faster, of course.
.find() is better performance wise as compared to your first selector
$('#tabs a[href="#ddd"]').tab('show');
, that is why pycharm changes it to the selector using .find()
$('#tabs').find('a[href="#ddd"]').tab('show');
http://vaughnroyko.com/the-real-scoop-on-jquery-find-performance/
The difference is that find() allows you to filter on a set of elements based on a selection you've already made, returning and array of elements if that's the case.
$('#tabs').find('a[href=“#ddd”]');
And it's a more specific way of searching for an element because you are saying "hey, go to #tabs and find me all a[href=“#ddd”] in there" instead of you saying "hey, find me all this guys $('#tabs a[href=“#ddd”]') in all the code that i have."
While, in most cases, the performance is the only difference, the difference in approach can also affect the outcome of your code, depending on what selectors you are using.
For example, $("table").find("tr:even").addClass("even"); will add the "even" class to the every other row in each individual table that gets returned. So, if the "even" class makes the text in the rows bold and you have two tables, each with 3 rows, you would get the following result:
this is table one, row 1
this is table one, row 2
this is table one, row 3
this is table two, row 1
this is table two, row 2
this is table two, row 3
In both cases, the 1st and 3rd row of each table (i.e., the "even" rows . . . don't get me started on JQuery's even filter, selecting the odd rows . . .) are bolded.
On the other hand, $("table tr:even").addClass("even"); will add the "even" class to every other row in the entire group of rows from all tables combined.
this is table one, row 1
this is table one, row 2
this is table one, row 3
this is table two, row 1
this is table two, row 2
this is table two, row 3
In this situation, the the 1st and 3rd row of second table are actually the 4th and 6th rows of the entire group of <tr> elements, so they are treated as "odd". The 2nd row of the second table, however, is the 5th row of the entire collection and, thus, is treated as "even" and is bolded.
Related
I was checking out a project where this line have been used in jquery script. I don't know why this is used. Can anyone please help me why this code has been used? I can share whole function if you need.
$("#table_data").find("tr:not(:first)").remove();
Let's understand statement step by step
$("#table_data").find("tr:not(:first)").remove();
$("#table_data")
It finds the table with id (table_data)
$("#table_data").find()
this will find the elements on the basis of selector here as selector is "tr:not(:first)" so it will find all the tr except first one. Finally there is .remove() that will remove the selected rows.
So the complete statement will remove all the rows except first one
Inside of #table_data, it searches for all trs that are not the first children of their container (that is, for every container, it selects all trs except the first), and then removes them from the container.
It selects all elements that do not match the given selector
here is the official documentation
so what it does is, it find elements in a table that are not first in a row, and removes them.
Im making a single page app on codepen.io (http://codepen.io/ntibbs/pen/ZbPPBm?editors=101) using knockout.js and jquery and while trying to figure out a way to select individual <li> elements that are added to my page using a foreach binding I noticed I can not use :nth-child() to do this. All the elements are considered :nth-child(1), not sure why its doing that. Are there any ways I could select an individual <li> element ?
The n in :nth-child() represents the depth, not the order. So :nth-child(1) selects every element that is the first child of its parent.
I'm not sure under what conditions you're trying to select a particular element, but I'll give you some options.
If each element already has a unique identifier, use that.
If you are already iterating through the set of elements, use logic in your loop to get the one you want.
If you can give me some more info on that, I can edit this accordingly.
All three operation are performing same thing.
can anyone explain the reason for different performance?
console.time('first');
for(guid in ALLGUID){
$('.'+guid).find('.cell-taskname').find('.cell-text').text()
}
console.timeEnd('first');
console.time('second');
for(guid in ALLGUID){
$('.'+guid).find('.cell-taskname .cell-text').text()
}
console.timeEnd('second');
console.time('third');
for(guid in ALLGUID){
$('.'+guid+' .cell-taskname .cell-text').text()
}
console.timeEnd('third');
first: ~3500.000ms
second: ~3700.000ms
third: ~38000.000ms (yes it is 38 second.)
Scenario Explanation: I have Huge table and I am iterating over all table rows. here Guid is unique key and class name of table row. cell-taskname is div inside every first column of table-row and .cell-text is span containing text.
It all has to do with the fact that selectors are processed right-to-left.
In your first and second examples, the main search of the DOM is only for the elements with the given GUID class. Then, you only search within those elements for the cells.
But in your last example, with a single selector, the engine does this:
Make a list of all .cell-text elements.
Remove ones that don't have an ancestor matching .cell-taskname from the list.
Remove any remaining ones that don't have an ancestor matching .<your-guid-here> from the list.
You can see how that takes a bit longer, if there are lots of .cell-text elements and only a few .<your-guid-here> elements. You've said that the .<your-guid-here> element is unique (a single row in a massive table), so the performance difference makes sense here.
More on right-to-left matching in this question and its answers.
lets say you have 100 rows with GUIDs, with each 100 .cell-taskname in them, with each 100 .cell-text in them.
100 GUIDS
10000 .cell-taskname
1000000 .cell-text
selectors are done right to left, so if you would do the 3rd example:
select all .cell-text (1m)
eliminate those that don't an ancestor .cell-taskname (still 1m)
eliminate those that don't have a higher ancestor of .guid (10k)
while if you do the first one:
select all .guid (1)
find all cell-taskname in that (100)
select all cell-text in those (10000)
in the 3rd (my first) scenario, you work with 2.01m elements
in the 1st (my second) scenario, you work with only 10101 elements. So that is why it's that much faster :-)
of course, these aren't exact performance numbers, but it does explain the concept behind selector optimizations...
I have been able to select the specific rows that I wanted but I can't select specific td from those tr.
I used this to select rows:
$('#time-slot tr:not(:first, :last)')
then within those selected rows I was trying to ignore the first and the last td but it is not working. I used similar approach as above
$('#time-slot tr:not(:first, :last) td:not(:first, :last)')
Any ideas how should I approach this problem. Btw I am trying to click and drag the mouse to paint the cells with user defined color.
I prefer using less of a string selector and more of the jQuery methods, as well as using the :first-child and :last-child selectors for your problem:
$("#time-slot").find("tr").not(":first-child, :last-child").find("td").not(":first-child, :last-child").addClass("active");
http://jsfiddle.net/7b4Ms/
Depending on what you are expecting, which you haven't explained very well, this may or may not be what you want. What this does is select all tr elements that are not the first and last. Inside of those matched tr elements, it selects all td elements that are not first and last. So basically, it doesn't select the outside ring of cells in the table.
Totally agree with Ian, just I prefer to use .children() instead of .find(). Because it can make a problem if you have nested tables.
$("#time-slot").find("tr").not(":first-child, :last-child").find("td").not(":first-child, :last-child").addClass("active");
changes to:
$("#time-slot").children("tr").not(":first-child, :last-child").children("td").not(":first-child, :last-child").addClass("active");
Try this (uses css selectors only):
$("#time-slot tr:not(:first,:last) td:not(:first-child,:last-child)").addClass("active");
Sample
I have a table with some radiobuttons in it. When i click on a radiobutton, i want to update two of the sorrounding containers ID attribute (a div and a table). The problem is, i need to go 4 and 6 levels up, and the only way i know how to do this is parent().parent().parent().parent() etc.
I am looking for a better solution, and was hoping someone could point me in the right direction. You can see an image of how the "parent-child" tree is here:
http://imageshack.us/photo/my-images/834/imgkz.png/
I already have a clickhandler etc set up.
Basicly i need to check if the table's id attribute is "answeredTable", if not i need to change it. Also i need to check if the div two levels up from the table is "answered", if not, i need to change that too.
Thanks
You can use .closest('#answeredTable') or .parents('#answeredTable').
Using .parent() only selects the first parent element upon the DOM tree, selecting .closest() will allow you to walk up to DOM tree and match until it finds the element, while .parents() will return the whole parentset of the DOM and match the element in the whole parentset.
You need to use .parents() that go through multiple level of the DOM
For instance, in your example, you could get the surrounding div with this code:
$("#Q_18_2015").parents("div#answered")
By the way, id should be unique, or else, your code might probably not work. You should use classes instead.
<div class="answered">
Thus, the code would become:
$("#Q_18_2015").parents("div.answered")
provided that Q_18_2015 is really a unique id
I think what you want to use is closest http://api.jquery.com/closest/
you can use .parents
$("element").parent(".parentClass")
parents will go up the DOM until finds the parent with class parentClass