Get nodes before, between and after tokens which are not tags - xpath

Is there a way using xPath to get the span nodes:
before "token_1"
between "token_1" and "token_2"
after "token_2"
<td>
<span class="user0">User 0</span>
<span class="user1">User 1</span> token_1
<span class="user2">User 2</span>
<span class="user3">User 3</span> token_2
<span class="user4">User 4</span>
</td>
The number of spans between each token is variable.

The span elements before the text node containing token_1 are its preceding-sibling::span elements so td/text()[contains(., 'token_1')]/preceding-sibling::span is a solution for that part; the ones following the text node containing token_2 are its following-sibling::span elements so td/text()[contains(., 'token_2')]/following-sibling::span is solution to that other part.
For the spans in between, in XPath 2 e.g. td/span[. >> ../text()[contains(., 'token_1')] and . << ../text()[contains(., 'token_2')]].
In XPath 1, it is a bit convoluted:
td/span[preceding-sibling::text()[normalize-space()][1][contains(., 'token_1')] and following-sibling::text()[normalize-space()][1][contains(., 'token_2')]]

Related

Make XPath stop at a certain depth?

I have the following HTML
<span class="medium bold day-time-clock">
09:00
<div class="tooltip-box first-free-tip ">
<div class="tooltip-box-inner">
<span class="fa fa-clock-o"></span>
Some more text
</div>
</div>
</span>
I want an XPath that only gets the text 09:00, not Some more text NOT using text()[1] because that causes other problems. My current XPath looks like this
("//span[1][contains(#class, 'day-time-clock')]/text()")
I want one that ignores this whole part of the HTML
<div class="tooltip-box first-free-tip ">
<div class="tooltip-box-inner">
<span class="fa fa-clock-o"></span>
Some more text
</div>
</div>
You can limit the level of descendant:: nodes with position().
So the following expression does work:
span/descendant::node()[2 > position()]
Adjust the number in the predicate to your needs, 2 is only an example. A disadvantage of this approach is that the counting of the descendants is only accurate for the first child in the descending tree.
Another approach is limiting the both: the ancestors and the descendants:
span/descendant::node()[3 > count(ancestor::*) and 1 > count(descendant::*)]
Here, too, you have to adjust the numbers in the predicates to get any useful results.
Use normalize-space() for select all non-whitespace nodes of the document:
//span[contains(#class, 'day-time-clock')]/text()[normalize-space()]
I think (if I understand you correctly) that
"..//div[contains(#class, 'tooltip-box')]/parent::span"
gets you there.

Getting single element with similar xpaths but with different same level, "neighboring" node

I'm trying to get the xpath of an element with a similar xpath to others but has a "neighbor" element that's different . Please see example below.
<div>
<div id='a'> </div>
<span> Text here </span> #this is what i'm trying to get
</div>
<div>
<div id='b'> </div>
<span> Text here </span>
</div>
I tried using //div//span, but this gives me the 2 spans. So i tried using //div//child::div[#id='a']//ancestor::div//child::span, but it doesn't look pleasant and looks repetitive. Does this have a better implementation?
try
//div[div[#id='a']]/span
it says get the span child node of all div nodes with child node div (with an #id equal to 'a').

Xpath / find all elements which contains attribute

I want to find all elements which have an attribute that contains the word: "aut".
For example:
<div aut20="one" class="model"> Some text </div>
<span aut="two" class="model_1" ng-one="two"> Some text 2 </span>
<a class="three"> some text 2 </a>
Then the xpath query result would be <div> and <span> elements because it has "aut20" and "aut".
//#*[contains(local-name(),'aut')]/..

How do I find if there's a second element exists with the same name with selenium using XPath?

I already know the Text of an element in the table:
string title1 = Driver.FindElement(
By.XPath(
".//[#id='ctl00_ContentPlaceHolder1_BreakdownGridView_ctl02_TitleLabel']"
)).Text;
How do I verify if there's a second element in the table that has the same text as title1?
Here is my Table structure:
<tr class="alt" style="background-color:White;height:110px;">
<td>
<span id="ctl00_ContentPlaceHolder1_BreakdownGridView_ctl03_WindowStartLabel" tabindex="1">
01/13/2013
</span>
</td>
<td>
<span id="ctl00_ContentPlaceHolder1_BreakdownGridView_ctl03_ShowLabel" tabindex="1">
Lea‌​der of the Pack
</span>
</td>
<td>
<span id="ctl00_ContentPlaceHolder1_BreakdownGridView_ctl03_TitleLabel">
Love at First Bite
</span>
</td>
<td>
<span id="ctl00_ContentPlaceHolder1_BreakdownGridView_ctl03_PremiereLabel">
01/12/2013 22:00
</span>
</td>
...
</tr>
After getting title1 try implement logic something like below to find how many elements are there with the same text
int count=driver.findElements(By.xpath("//table[#id='urTable']//*[text()='"+title1+"']")).size();
Based on the count it is easy to find how many elements are there with same text.
In your table structure, doing an xpath query as below would select all the span tags.
//tr/td/span
Now, xpath can be used to select the span tags with specific text as follows:
//tr/td/span[text() = "Text you're matching against"]
/* in your case*/
//tr/td/span[text() = title1]
This is to get spans consist that text....(partial match)
int count= driver.FindElements(
By.XPath(
".//span[contains(text(),'"+titleOneText+"')]"
)).size();
This is to get spans text is same....(full match)
int count= driver.FindElements(
By.XPath(
".//span[text()='"+titleOneText+"']"
)).size();
Based on your question if same xpath value having 2 elements. Just see the static value of particular element and take as xpath. it will work definitely. for example
save & cancel button having same xpath value
driver.find_element_by_xpath("//*[#id='id_sav']")-save
driver.find_element_by_xpath("//*[#id='id_sav']")-cancel
here we can use static element value
driver.find_element_by_xpath("//a[text()='save']")-save
driver.find_element_by_xpath("//a[text()='cancel']")-cancel

Retrieving text from an element with children

I am trying to get the text "Weeeeee" but when i use //td[#class='something']/text() I got nothing
<td class="something">
<a href='http://www.google.com'>Google</a>
Weeeeee
<div>
<a>something</a>
</div>
</td>
Try
//td[#class='something']/text()[normalize-space() != ''][1]
as there are three text nodes in your example, the first and the last one consist of whitespace only.
Highlighted with square brackets:
<td class="something">[\n
----]<a href='http://www.google.com'>Google</a>[\n
----Weeeeee\n
----]<div>
<a>something</a>
</div>[\n
]</td>

Resources