mckrex
mckrex

Reputation: 45

Selenium XPath - how to find immediately preceding element

I am running tests in Java Selenium where I find an element, and once found need to find the immediately preceding element. The code needs to be dynamic because I don't know if the element I start from is the 3rd out of 5, the 6th out of 10, or the 1st out of 2.

The problem is, using preceding-sibling from xpath, I get all elements preceding the found element, not the one immediately preceding it. For example, this is my xml:

<div>
    <input formcontrolname="myValues" id="value1" type="radio" value="VALUE1" />
    <label for="value1">Value 1</label>
    <input formcontrolname="myValues" id="value2" type="radio" value="VALUE2" />
    <label for="value2">Value 2</label>
</div>

Lets say I have found the 'Value 2' label as a WebElement, then I want to do next is find the specific input immediately preceding that label (id='value2'). Since it has to be relative to the label I've found, I can't just find by that id; I don't really know the id, just that the input is supposed to precede the label. I thought I could use this:

WebElement input = element.findElement(By.xpath("preceding-sibling::input"));

But that xpath actually returns all the inputs that are siblings of the label. So, in the example, I get an array of two inputs returned, both 'value1' and 'value2'. Selenium sets the value of input to be the input with id='value1', the first in the returned array.

So my question is, is there an xpath that will return the one element that immediately precedes a given element?

Upvotes: 3

Views: 19094

Answers (3)

undetected Selenium
undetected Selenium

Reputation: 193308

As per the HTML you have shared to locate the <input> with id="value2" with respect to <label> with Value 2 you can use the following xpath :

.//preceding::input[1]

Update

I still feel you haven't been able to express/put-up your exact use-case/requirement properly. A possible use-case for you may be to invoke click() on a Radio Button i.e. on a <input> tag with respect to a Label i.e. a <label> tag. If this is your usecase a better way would be to write a function as follows :

public void click_radio_button(String label_text)
{
    driver.findElement(By.xpath("//label[.='" + label_text + "']//preceding::input[1]")).click();
}

Now from your main() or @Test annotated class you can call the function click_radio_button() passing the Label Text as an argument to click() on the respective Radio Button as follows :

click_radio_button("Value 1")
click_radio_button("Value 2")

Finally, why should you have chosen preceding over preceding-sibling ?

As per the HTML all of the <input> and <label> nodes are on the same step as follows :

<div>
    <input formcontrolname="myValues" id="value1" type="radio" value="VALUE1" />
    <label for="value1">Value 1</label>
    <input formcontrolname="myValues" id="value2" type="radio" value="VALUE2" />
    <label for="value2">Value 2</label>
</div>

Ideally,

  • preceding-sibling should have the prototype of preceding-sibling::parentNode/childNode[n] (which is not your usecase)
  • preceding should have the prototype of preceding::node[n] (which exactly matches your usecase)

    Here you can find a detailed discussion on Which should I use: preceding:: or preceding-sibling::?

Upvotes: 0

Chris Brocious
Chris Brocious

Reputation: 161

I'm a bit confused on what you're telling to the driver to reference. 'findElement(By.xpath("preceding-sibling::input"));' isn't specifying WHAT it should precede.... so if all you're looking to do is find everything preceding any element with an id of 'value,' you're code should look something like this:

driver.find_elements_by_xpath("//*[contains(@id, 'value')]]/preceding-sibling::input");

Now the syntax assumes you know where your semi-colons, parentheticals, etc should be... I apologize in advance if my syntax isn't right. I write selenium, but only in Python.

Upvotes: 0

Andersson
Andersson

Reputation: 52685

You can get required result by specifying the index:

./preceding-sibling::input[1]

In this case you'll get the first (immediate) preceding-sibling of type "input"

Upvotes: 4

Related Questions