Select Comment with xPath by Comment-Text - xpath

How can I select the next node after an defined comment? I know the text of the comment and need the next nodes.
Best regards
Christoph

Supposing the comment is a child of the current node, and the comment's text is "comment-text", then the following XPath expression selects the first element (sibling) that follows the comment node:
comment()[. = 'comment-text'][1]/following-sibling::*[1]
If we want the following element to be selected (regardless if it is sibling of the comment or not), the corresponding XPath expression is:
comment()[. = 'comment-text'][1]/following::*[1]

Related

What is the difference between xpath //a and .//a in Selenium Webdriver [duplicate]

While finding the relative XPath via Firebug : it creates like
.//*[#id='Passwd']--------- what if we dont use dot at the start what it signifies?
Just add //* in the Xpath --
it highlights --- various page elements ---------- what does it signify?
Below are XPaths for Gmail password fields. What is significance of * ?
.//*[#id='Passwd']
//child::input[#type='password']
There are several distinct, key XPath concepts in play here...
Absolute vs relative XPaths (/ vs .)
/ introduces an absolute location path, starting at the root of the document.
. introduces a relative location path, starting at the context node.
Named element vs any element (ename vs *)
/ename selects an ename root element
./ename selects all ename child elements of the context node.
/* selects the root element, regardless of name.
./* or * selects all child elements of the context node, regardless of name.
descendant-or-self axis (//*)
//ename selects all ename elements in a document.
.//ename selects all ename elements at or beneath the context node.
//* selects all elements in a document, regardless of name.
.//* selects all elements, regardless of name, at or beneath the context node.
With these concepts in mind, here are answers to your specific questions...
.//*[#id='Passwd'] means to select all elements at or beneath the
context node that have an id attribute value equal to
'Passwd'.
//child::input[#type='password'] can be simplified to
//input[#type='password'] and means to select all input elements
in the document that have an type attribute value equal to 'password'.
These expressions all select different nodesets:
.//*[#id='Passwd']
The '.' at the beginning means, that the current processing starts at the current node. The '*' selects all element nodes descending from this current node with the #id-attribute-value equal to 'Passwd'.
What if we don't use dot at the start what it signifies?
Then you'd select all element nodes with an #id-attribute-value equal to 'Passwd' in the whole document.
Just add //* in the XPath -- it highlights --- various page elements
This would select all element nodes in the whole document.
Below mentioned : XPatht's for Gmail Password field are true what is significance of * ?
.//*[#id='Passwd']
This would select all element nodes descending from the current node which #id-attribute-value is equal to 'Passwd'.
//child::input[#type='password']
This would select all child-element nodes named input which #type-attribute-values are equal to 'password'. The child:: axis prefix may be omitted, because it is the default behaviour.
The syntax of choosing the appropriate expression is explained here at w3school.com.
And the Axes(current point in processing) are explained here at another w3school.com page.
The dot in XPath is called a "context item expression". If you put a dot at the beginning of the expression, it would make it context-specific. In other words, it would search the element with id="Passwd" in the context of the node on which you are calling the "find element by XPath" method.
The * in the .//*[#id='Passwd'] helps to match any element with id='Passwd'.
For the first question: It's all about the context. You can see Syntax to know what '.', '..' etc means. Also, I bet you won't find any explanation better than This Link.
Simplified answer for second question: You would generally find nodes using the html tags like td, a, li, div etc. But '*' means, find any tag that match your given property. It's mostly used when you are sure about a given property but not about that tag in which the element might come with, like suppose I want a list of all elements with ID 'xyz' be it in any tag.
Hope it helps :)

Xpath to go back to sibing td

I am trying to back to to previous td but to no avail, can you help
//*[#class='ein' and contains(.,'aaaa')] gets me to td but need to select the previous td-tried below but did not work
//*[#class='ein' and contains(.,'aaaa')][preceding-sibling::td]
Remember /X means "select X", while [X] means "where X". If you want to select preceding siblings, rather than testing whether they exist, use /.
It's impossible to say for certain without seeing the input HTML but I suspect that instead of
//*[#class='ein' and contains(.,'aaaa')][preceding-sibling::td]
you need something like
//*[#class='ein' and contains(.,'aaaa')]/preceding-sibling::td[1]
to navigate from each node selected by the initial expression to its nearest preceding td. Your first attempt will select exactly the same nodes as
//*[#class='ein' and contains(.,'aaaa')]
but only if they have at least one preceding-sibling element named td.
Use // after the element you found
Instead of preceding-sibling, just use preceding
//*[#class='ein' and contains(.,'aaaa')]//preceding::td[1]

XPath element number returns all elements instead than first

I am using the following code to access a link (for phpunit/selenium):
//td[normalize-space() ='Test title 2']/following-sibling::td[3]/a[.='delete']
using an XPath checker in FireFox it returns 7 elements (because there are 7 links matching "test title 2"), but when I add [1] at the end:
//td[normalize-space() ='Test title 2']/following-sibling::td[3]/a[.='delete'][1]
it still returns 7 links. What am I doing wrong here?
When you add [1] in the end of your expression, you select the first a child of each ...td[3] (i.e. 7 a child nodes). You can change your query to:
xpath=(//td[normalize-space() ='Test title 2']/following-sibling::td[3]/a[.='delete'])[1]
or in case you use webdriver (xpath prefix is not needed):
(//td[normalize-space() ='Test title 2']/following-sibling::td[3]/a[.='delete'])[1]
This will select the first a from the entire set of a children of ...td[3] elements.
Refer to XPath Examples for more tutorials.
As the spec says:
The location path //para[1] does not mean the same as the location
path /descendant::para[1]. The latter selects the first descendant
para element; the former selects all descendant para elements that are
the first para children of their parents.
Therefore,
//td[normalize-space() ='Test title 2']/following-sibling::td[3]/descendant::a[.='delete'][1]
will do a better job in your case.

xpath choose first table

I have xpath
page.search("//table[#class='campaign']//table")
which returns two tables.
I need to choose only first table. This line doesn't work:
page.search("//table[#class='campaign']//table[1]")
How to choose only first table?
This bugged me, too. I still don't exactly know why your solution does not work. However, this should:
page.search("//table[#class='campaign']/descendant::table[1]")
EDIT: As the docs say,
"The location path //para[1] does not mean the same as the location
path /descendant::para[1]. The latter selects the first descendant
para element; the former selects all descendant para elements that are
the first para children of their parents."
Thanks to your question, I finally understood why this works this way :). So, depending on your structure and needs, this should work.
Instead of using an XPath expression to select the first matching element, you can either find all of them and then pare it down:
first_table = page.search("//table[#class='campaign']//table").first
...or better yet, select only the first by using at:
first_table = page.at("//table[#class='campaign']//table")
Note also that your expression can be found more simply by using the CSS selector syntax:
first_table = page.at("table.campaign table")

Modify XPath to return second of two values

I have an XPath that returns two items. I want to modify it so that it returns only the second, or the last if there are more than 2.
//a[#rel='next']
I tried
//a[#rel='next'][2]
but that doesn't return anything at all. How can I rewrite the xpath so I get only the 2nd link?
Found the answer in
XPATH : finding an attribute node (and only one)
In my case the right XPath would be
(//a[#rel='next'])[last()]
EDIT (by Tomalak) - Explanation:
This selects all a[#rel='next'] nodes, and takes the last of the entire set:
(//a[#rel='next'])[last()]
This selects all a[#rel='next'] nodes that are the respective last a[#rel='next'] of the parent context each of them is in:
//a[#rel='next'][last()] equivalent: //a[#rel='next' and position()=last()]
This selects all a[#rel='next'] nodes that are the second a[#rel='next'] of the parent context each of them is in (in your case, each parent context had only one a[#rel='next'], that's why you did not get anything back):
//a[#rel='next'][2] equivalent: //a[#rel='next' and position()=2]
For the sake of completeness: This selects all a nodes that are the last of the parent context each of them is in, and of them only those that have #rel='next' (XPath predicates are applied from left to right!):
//a[last()][#rel='next'] NOT equiv!: //a[position()=last() and #rel='next']

Resources