XPath is one of the most important locators we use in Selenium when we need to interact with elements that don’t have unique IDs, names, or classes. Some XPaths are quite straightforward, but there may be cases when the elements cannot be easily identified, so we need to single them out based on specific characteristics or relations to other elements. For these cases, we’ll need to use advanced XPath locators.
Locating the last element
XPath allows us to use the index of the element, to locate the nth element with a certain attribute. For example, //*[@class='MyClass'][1]
will return the first element with the class name ‘MyClass’. But we can also identify the last element with this attribute, even if we don’t know beforehand the total number of elements. For this, XPath allows us to use the method [last()]
. We can also use [last()-1]
to find the second to last, and so on. Let’s check an example – we have this table:
With this HTML code:
If I need to get the last person’s first name, without knowing the total number of rows in the table or the text value inside the cell, I need to identify the first cell (td
element) of the last row (tr
element). In Xpath, this will translate to: //tr[last()]/td[1]
Locating elements by parents
The HTML document has a tree structure, which means that elements are ancestors, descendants, parents, children, and siblings 👨👩👦👦 How does the parent axis work? Let’s see another example:
If we want to identify the paragraph that contains the image (may not be a probable test case, but for the sake of the example, let’s say that’s what we need), we can use the img
element and its unique alt attribute and transverse the HTML up from there:
//img[@alt='smiley']/parent::p
.
This means that we identify the p
element that is the parent of //img[@alt='smiley']
.
A shorter way to write this (but a bit harder to read) is: //img[@alt='smiley']/..
Using ancestors in XPath
Ancestors in XPath have a broader range than the parent axis, because they also take into account higher levels inside the HTML tree. In our example above, we can replace parent
with ancestor
and we will get the same result. However, if we want to go higher than the parent level, or we are unsure of the relationship between the elements, we will use the ancestor
axis. So we can have:
//img[@alt='smiley']/ancestor::p
which will return the paragraph parent of the image,
//img[@alt='smiley']/ancestor::div
– returns the div ancestor, or
//img[@alt='smiley']/ancestor::*
which matches all the image’s ancestors, regardless of their tag.
Working with siblings
The sibling axis is used when we need to identify an element based on information from another element at the same level, with the same parent. We can go back to our first table and, try to find the age of the person named Madison. The name cell and the age cell are siblings (they are both td
elements under the tr
parent), so we can use this to create our XPath. Basically what we are looking for is the cell preceded by the one with the text ‘Madison’:
//td[preceding-sibling::td[text()='Madison']]
Or, in other words, the cell following the one with the text ‘Madison’:
//td[text()='Madison']/following-sibling::td
Both XPaths will return the same unique element.
More advanced Xpath locators
Advanced XPath: “Cousins”
Not really the official term, but, similar to siblings, these are elements located at the same level inside the HTML tree, except instead of having the same parent, they have a common ancestor on a higher level. I’ll reuse the HTML from the first table, but the content of the cells will be inside div tags:
Now if I want to identify the age cell again, based on the ‘Madison’ name value, I won’t be able to use the sibling axis, because the ‘Madison’ div and the ’22’ div each have their own td
parent. However, I can make use of the fact that the parent td
s are siblings, and so my resulting XPath should be:
//div[text()='Madison']/../following-sibling::td/div
or
//div[text()='Madison']/ancestor::td/following-sibling::td/div
or
//div[text()='Madison']/parent::td/following-sibling::td/div
Advanced XPath: “Uncles”
Let’s look at a slightly different HTML code:
The information is the same as above. But because the bold tags are added only around the names, to get the age of the person named ‘Madison’, I need to go to the name’s parent/ancestor, and then select the parent’s sibling. This makes the relationship between the age element and the name element an “uncle/nephew” one. The resulting XPath is almost the same as above, but simplified because we don’t need to go down another level in the last td
. So our XPath will be:
//b[text()='Madison']/../following-sibling::td
or
//b[text()='Madison']/ancestor::td/following-sibling::td
or
//b[text()='Madison']/parent::td/following-sibling::td
Nice article. Not sure if you ever get a chance to try SelectorsHub, very nice utility to write xpath and verify.