How to prevent Xpath recursion - xpath

Given I have this (unknown) document structure, how do I write xpath to select div1 and div2, i.e. all divs, but not recursivelly (no divs, contained anywhere within another divs)?
I couldn't find any documentation that would point me in this direction, all I could manage is to select ALL divs, i.e. div1, div2 and div3 (with //div expression), but I want to exclude div2 here as it is the descendant div of another one.
(I need a generic solution to select tags not recursivelly, the ids here are for explanatory purposes only.)
...some unknown structure with no divs...
<div id="1">
...some unknown structure with no divs...
<div id="2"></div>
...some unknown structure with no divs...
</div>
...some unknown structure with no divs...
<div id="3"></div>
...some unknown structure with no divs...

If you select //div[not(ancestor::div)] you select all div elements that don't have any ancestor also being a div.
If you have access to XPath 3.1 or 3.0 you can also use the outermost function https://www.w3.org/TR/xpath-functions/#func-outermost as it "returns every node within the sequence that does not have another node within the sequence as an ancestor" so "the expression outermost(//div) returns those div elements that are not contained within further div elements".

Related

xPath: fetch element with an attribute containing the text of another element

Given I have the following HTML structure:
<button aria-labelledby="ref-1" id="foo" onclick="convey(event)">action 2</button>
<div class="anotherElement">foobar</div>
<div id="ref-1" hidden>target 2</div>
I would like to fetch button by its aria-labelledby attribute. I tried the following options:
//*[#aria-labelledby=string(/div[#id="ref-1"]/#id)]
//*[#aria-labelledby = string(.//*[normalize-space() = "target 2"]/#id)]
//*[#aria-labelledby = .//*[normalize-space() = "target 2"]/#id]
But wasn't able to fetch the element. Anyone has an idea what the right xPath could be?
Edit: simply put: how do I fetch the button element if my only information is "target 2", and if both elements can be randomly located?
//button[#aria-labelledby='ref-1']
or
//button[#aria-labelledby=(//*/#id)]
or
//button[#aria-labelledby=(//*[contains(.,'target 2')]/#id)]
or
//button[#aria-labelledby=(//*[contains(text(),'target 2')]/#id)]
?
Since button and div are the same level siblings here you can use preceding-sibling XPath expression like this:
//div[text()='target 2']//preceding-sibling::button
pay attention with with your actual XML this will match 2 button elements.
To make more precise math I think we will need to be based on more details, not only the target 2 text

How to get descendants with a specific tag name and text in protractor?

I have the following structure (it's just for sample). In protractor, I am getting the top element by id. However, the other elements do not have id's. I need to get the "label" element that contains the text '20'. Is there an easy way in protractor to select the element with a specific tag that contains a specific text from all the descendants of a parent element?
<pc-selector _... id="Number1">
<div ...></div>
<div ...>
<div ...>
<check-box _...>
<div _ngcontent-c25="" ...>
<label _ngcontent-c25="">
<input _ngcontent-c25="" type="checkbox">
<span _ngcontent-c25="" class="m-checkbox__marker"></span>
20 More text to follow</label>
</div>
</check-box>
</div>
</div>
</pc-selector>
I could't find anythitng, so I have tried with xpath, but protractor complains that my xpath is invalid:
parentElement = element(by.id('Number1'));
return parentElement.element(by.xpath(".//label[contains(text(),'20'))]"));
Any ideas?
You have an additional bracket in your [contains(text(),'20'))] which is likely causing you issue but there are multiple other ways this can be achieved using a single XPath or chaining other locators.
The process is that you must find the div with the correct id first and then locate the label that is a child of it.
//Xpath
element(by.xpath("//pc-selector[#id='Number1']//label[contains(text(),'20')]"));
//Chained CSS
element(by.id('Number1')).element(by.cssContainingText('label','20'));
You also may be interested to learn about xpath axes which can allow us to do very dynamic selection.
You can use the direct xpath to access the label.
element(by.xpath("//*[#id='Number1']//label"));

Selenium Webdriver / XPath: How to find element by attribute and the text of its children

Given html:
<div class="class1">
<div class="class2">1 2 3</div>
<div class="class3">a b c</div>
</div>
As i have several div elements in my html which uses the class "class1" and none has a id i want to find/fetch this parent element by the text of its children.
I tried different variants like
By.xpath("//div[contains(#class, 'class1') "
+ "and text()[contains(.,'1 2 3')] "
+ "and text()[contains(.,'a b c')]]"));
but nothing seems to work yet.
In the example above i guess the text of the class1 element is checked but not of its children.
Can anybody help?
So you're looking for a div with class class1 that has children with texts 1 2 3 and a b c. From your example of what you've tried, I'm assuming there are no further conditions (eg class) on the children:
//div[#class='class1' and div/text()='1 2 3' and div/text()='a b c']
You can make those children node names into * if you don't care whether they are divs or not. You can make the children node names prefixed by descendant:: if you don't require them to be direct children.
Try any of these below mentioned xpath.
Using class attribute of <div> tag.
//div[#class='class2']/..//div[#class='class3']/..//parent::div[#class='class1']
Explanation of xpath: First locate both child elements using the class attribute of <div> tag and then move ahead with parent keyword with <div> tag along with class attribute.
OR
Using text method along with <div> tag.
//div[text()= '1 2 3']/..//div[text()= 'a b c']/..//parent::div[#class='class1']
Explanation of xpath: First locate both child elements using the text method of <div> tag and then move ahead with parent keyword with <div> tag along with class attribute.
These above xpath will locate your parent element <div class="class1">

Need a xpath : where parent having multiple child, but i required only parent value

In below code: parent "div" having three child "span", "script" and "span". but i required the value of Parent "div" which "N/A". "N/A" not comes under any attribute of div. Its just a value of parent "div".
<div class="ah-text-align-right ah-font-xsmall" style="">
<span id="_dcmanageinvestmentsportlet_WAR_ahdcmnginvportlet__FDROR_110hidden" style="display:none">
<script type="text/javascript">
<span class="ah-float-left">
N/A
</div>
For getting parent element you can use double dot .. after child element xpath.
For getting text of an element you can use xpath text() function, but depending on implementation of xpath in whatever environment and code you use, it might be unavailable. Note, that text of an element will return actual text node of this element as well as all text nodes of child elements.
For your case if you search a parent of a span with ah-float-left class, then xpath should be something like following:
//span[#class='ah-float-left']/..
For getting text of a parent, you'll need following:
//span[#class='ah-float-left']/../text()
Note: looking elements up by class name may return you a collection of elements which in turn will return you collection of parent elements and collection of parent nodes texts, which may not be desired. I would recommend lookup child element by id, since xhtml prescribes that elements ids are unique. Thus, an xpath for a parent div should better look like following:
//span[#id='_dcmanageinvestmentsportlet_WAR_ahdcmnginvportlet__FDROR_110hidden']/..

Retrieving a parent tag with a given attribute that contains a subelement by using XPath

How I can retrieve multiple DIVs (with a given class attribute "a") that contain a span tag with a class attribute "b" by using Xpath?
<div class='a'>
<span class='b'/>
</div>
The structure of my XML is not defined so basically the span could be at any level of the div and the div itself could be at any level of the XML tree.
This should work:
//div[#class='a'][span/#class='b']
// means search anywhere if it starts the expression.
If the span is deeper in the div, use descendant:: which can be shortened to // again:
//div[#class='a'][.//span/#class='b']

Resources