So I have a document with multiple divs.
using the path '//div[1]' seems? to be returning multiple elements.
thoughts?
// is a shorthand in XPath for /descendant-or-self::node()/ including the leading and trailing slashes. So //div[1] fully expanded means
/descendant-or-self::node()/child::div[1]
i.e. every div element anywhere in the document that is the first div child of its respective parent.
If you want just the first div in the whole document then you need parentheses:
(//div)[1]
or use the descendant:: axis explicitly
/descendant::div[1]
Related
I have a recursive function that creates divs inside divs on demand by the user, but I've stumbled on a problem where I need to separate those divs as in a specific manner, similar to "layers".
Example:
I have created a div, firstborn - and inside it, the user created another div (lets call this one child div). Inside this secondary div, the user created yet another div (and this one grandchild div) - I need to know the number of child divs of the firstborn div, and the number of child divs that the child div has (grandchild divs)
I've stumbled upon this issue because the way I was trying to get them was by:
numberOfFirstbornDivs = document.getElementsByClassName("firstBornDiv").length
numberOfChildrenDivs = document.getElementsByClassName("recursivelyCreatedDiv")[index].getElementsByClassName("recursivelyCreatedDiv").length
What I wanted was the number of divs named "recursivelyCreatedDiv" only under the first "layer", but instead, I get the number of divs from all subsequent "layers" (in the given example I wanted to have:
numberOfFirstbornDivs = 1 and
numberOfChildrenDivs = 1,
but instead I get:
numberOfFirstbornDivs = 1 and
numberOfChildrenDivs = 2).
I understand this happens because I name my divs by the same ClassName, but I don't know how to workaround this as they are created recursively (and on demand by the user).
To only select direct childs of an element you can use the '>' css selector instead of getting elements by class name.
For example, .firstBornDiv > .recursivelyCreatedDiv will target all elements with the recursivelyCreatedDiv class that are direct children of a div with the firstBornDiv class
Edit 1: To be more precise, you can use the querySelectorAll() method with any css selector to get the array of elements targeted by your selector which is what you would need with this answer.
I'm trying to figure out how to get text using XPath and exclude some tags.
Let's say (for illustration) I want to get all text from this page's body tag (so all visible text), but I don't want my text to contain text from tags with class="comment-copy" i.e. I don't want text to include comments.
I tried this but it doesn't work. It returns text including comments.
//body//text()[not(*[contains(#class,"comment-copy")])]
Do you have any idea?
EDIT:
Probably figured it out but maybe there are better or faster approaches so I won't delete the question.
//body//text()[not(ancestor-or-self::*[contains(#class,"comment-copy")])]
You were very close.
Just change
//body//text()[not(*[contains(#class,"comment-copy")])]
to
//body//text()[not(contains(../#class,"comment-copy"))]
Note that this will only exclude immediate children text() nodes of comment-copy marked elements. Your follow-up XPath will exclude all descendant text() nodes beneath comment-copy marked elements.
Note: You might want to beef up the robustness of the #class test; see Xpath: Find element with class that contains spaces.
I have two polymer elements like
<moviegrep-element></moviegrep-element>
<custom-card-element></custom-card-element>
In moviegrep-element I got an array of objects called results. I want to use the results in my custom-card-element. How does it work?
Use Polymers data-binding:
<moviegrep-element results="{{sharedResults}}"></moviegrep-element>
<custom-card-element results="{{sharedResults}}"></custom-card-element>
This assumes that both of your elements publish the results property as an attribute. Changes to the results property in one element are then propagated to the results property in the other element.
This also assumes that your elements are itself inside a Polymer element. Otherwise you need an auto-binding template element
Can someone help me understand the difference between the two following XPath queries:
A: //table[#id="xyz"]//tr[//a[contains(text(), "Alice")]]
B: //table[#id="xyz"]//tr[*/a[contains(text(), "Alice")]]
(A) appears to select all the tr's in the table regardless of whether it has an a descendent with the text "Alice".
(B) meanwhile does what I expect in only selecting the trs with a descendents containing the text "Alice".
As an aside question, is there a more elegant way of writing the above?
You would need to use //table[#id="xyz"]//tr[.//a[contains(text(), "Alice")]] or //table[#id="xyz"]//tr[descendant::a[contains(text(), "Alice")]] to make sure that in the first expression the path in square brackets is relative to the tr. With your current //tr[//a] inside the predicate the selection //a starts from the document node, the root node, and is not relative to the tr.
//a selects all a elements anywhere in the document.
*/a selects all a elements that are grandchildren of the context node.
.//a selects all a elements that are descendants of the context node at any depth
./a (or just a) selects all a elements that are children of the context node.
It's vital to understand the notion of context. In a predicate such as tr[XXXX] the context node for evaluating XXXX is the tr element that you are testing.
I want to access an element using a DOM hierarchy Node structure, through its parent nodes.I am trying to find the DOM hierarchy through firebug; want something like, <parent_node1>.<child_node1>.<child_node2> (not by document.getElementByID, getElementbyname) to access an element.
I want to automate a scenario like, I have column headers and corresponding values. Want to test, whether the values present under each column header, is correct...
I am thinking of using DOM as a method of automating this case...But, how can I find the DOM hierarchy...?
What I see through Inspect Element in Firebug is something like, list of events, elements and is not looking like a hierarchy node structure...Can somebody help in this regard please?
As discussed, you probably mean the DOM Element properties like element.childNodes, element.firstChild or similar.
Have a look at the DOM Element property reference over at JavaScriptKit, you'll get a good overview there how to access the hierarchy.
var currentTD = document.getElementsByTagName("td")[0];
var currentTable = document.getElementsByTagName("table")[0];
currentTD.parentNode // contains the TR element the TD resides in.
currentTable.childNodes // contains THEAD TBODY and TFOOT if present.
DOM Tables even have more properties like a rows collection and a cells collection.
A reminder of caution: Beware that these collections are live collections, so iterating over them and accessing collection.length in each iteration can be really slow because to get the length, the DOM has to be queried each time.
document.getElementById and document.getElementByTagname are using the DOM. They take an object within the DOM (specifically the document object, though you can also call both of those on elements) and return an object which is a single element or a collection of zero or more elements, respectively. That's a DOM operation. From there you can do other DOM operations on the results like getting children, parents or siblings, changing values etc.
All DOM operations come down to:
Take a starting point. This is often document though it's so often that the first thing we do is call document.getElementById or document.getElementByTagname and then work from the result that we could really consider that the starting point.
Find the element or elements we are interested in, relative to the starting point whether through startingPoint.getElementById* or startingPoing.getElementByTagname perhaps combined with some test (e.g. only working on those with a particular classname, if they have children of particular types, etc.
Read and/or change certain values, add new child nodes and/or delete nodes.
In a case like yours the starting point will be one or more tables found by document.getElementById(someID), document.getElementById(someID).getElementsByTagname('table')[0], or similar. From that table, myTable.getElementsByTagname('th') will get you the column headings. Depending on the structure, and what you are doing with it, you could just select corresponding elements from myTable.getElementsByTagname('td') or go through each row and then work on curRow.getElementsByTagname('td').
You could also just use firstChild, childNodes etc. though it's normally more convenient to have elements you don't care about filtered out by tagname.
*Since there can only be one element with a given id in a document, this will return the same if called on any element higher in the document hierarchy, so we normally just call this on document. It can be useful to call it on an element if we want to do something if the element is a descendant of our current element, and not otherwise.