5

I have a structure such as this:

<div class="Container">
  <div class="HighlightContainer">
    <div class="NodeTextHighlightContainer">
      <span class="TreeItemSelected">Products</span>
    </div>
    <button class="ContainerSelectedMenu" type="button"></button>
  </div>
</div>

Because of how the DOM behaves and trying to stay dynamic, I can only target the span that contains text Products. using something like:

Driver.FindElement(By.XPath("//div[contains(@class, 'Container')]/descendant::span[text() = 'Products']"));

However, I need to target the button where class="ContainerSelectedMenu" based of that span element, what is the best approach? Something like getting the parent div of the child of Container then finding the button element.

saltillokid11
  • 83
  • 1
  • 1
  • 6

2 Answers2

11

I've found different ways to do this by traversing back up and down which works fine, but my preference now is this approach:

xpath = "//div[contains(@class, 'Container') and descendant::span[text() = 'Products']]//button";

Basically, you put the descendant with text() = 'Products' as part of the requirement for the div tag you really want, which is the parent. Then you can just search for the button easily with a //button, or //button[@class='ContainerSelectedMenu']

You actually don't need the descendant axes here, so it can be simplified a bit with this:

xpath = "//div[contains(@class, 'Container') and .//span[text() = 'Products']]//button";

In English...

  • Find a div that
  • 1. has a @class that contains "Container" and
  • 2. has a descendant span element with the text `Products
  • Find a descendant of that div that is a button
mrfreester
  • 1,981
  • 2
  • 17
  • 36
3

One way is to target the span, then go up to the ancestor div, and then back down to the element with the matching class...

//span[normalize-space()='Products']/ancestor::div[contains(@class,'Container')]//*[contains(@class,'ContainerSelectedMenu')]

Another way is to target the div, then the span, then go up the two levels and then back down to the element with the matching class...

//div[contains(@class,'Container')]//span[normalize-space()='Products']/../../*[contains(@class,'ContainerSelectedMenu')]

Yet another way (similar to @mrfreester) is to match the div, test the span, and go directly to the element with the matching class...

//div[contains(@class, 'Container') and .//span[normalize-space()='Products']]//*[contains(@class,'ContainerSelectedMenu')]

All three of these match the button.

Daniel Haley
  • 51,389
  • 6
  • 69
  • 95