Different XPath writing techniques for locating elements in Selenium Web Driver

Monil Joshi
9 min readOct 19, 2021

--

In the previous articles we have seen strategies to locate element using different locator type and CSS selectors in detail.

Introduction to Locators & Different Locator Strategies in Selenium

CSS Selectors for locating elements in Details

In this article we will see most important locator type to locate element. With XPath you can almost locate any element in the DOM even though it does not class, name, or id any of these attributes. We can reach to the desired element using Xpath by using one by onehierarchy. XPath uses “path like” syntax to identify and navigate nodes in an XML document

But it is a last choice as it is the most complicated method of identifying elements. Also even though there is slight change in hierarchy in html tag or unexpected addition of HTML code can make XPath invalid. XPath can be primarily written as Absolute XPath or Relative xPath.

I am referring to a demo website to write this blog https://www.techlistic.com/p/selenium-practice-form.html?m=1. Also for some XPath techniques I have given HTML code first and referring same to write XPath using that XPath technique

You can get the by XPath ChromeDev tools only. Once you are inspect element screen.

  1. Locate the element.
  2. Right click on HTML code of that element
  3. Click on Copy
  4. Select what type of XPath you want to copy? XPath or Full Xpath

Question comes in to your mind what is Xpath and full xpath. Why there are 2 type XPaths to confuse us. These two XPaths are Absolute XPath and relative XPath.

Absolute XPath: It is the direct way to find the element from the root node/tag of the page i.e. html, but the disadvantage of the absolute XPath is that if there are any changes made in the path of the element then that XPath gets failed.The key characteristic of XPath is that it begins with the single forward slash(/) ,which means you can select the element from the root node.

Below is the absolute XPath for first radio button

/html/body/div[1]/div[3]/div/div[2]/main/div/div[1]/div[1]/article/div/div/div[3]/div[1]/div/div[15]/input[1]

Relative XPath :Starts from the middle of HTML DOM structure. It starts with double forward slash (//). It can search elements anywhere on the webpage, means no need to write a long XPath and you can start from the middle of HTML DOM structure. Relative XPath is always preferred as it is not a complete path from the root element.

Below is the relative XPath for first radio button

//*[@id="exp-0"]

We can write XPath like this

driver.findElement(By.xpath("//*[@id="exp-0"]"));

There are different ways to write XPath.

1. XPath can be written with help of tagName, attribute (including ClassName and id), attribute value. This will be written as

driver.findElement(By.xpath("//tagName[@attribute="attribute_value"]"));

Here tagName will get searched from the top of DOM. Now you can replace it tagName with * so that it will check for any tag in the DOM hierarchy with given attribute and attribute value.

2. XPath with Text() : This is in build function in Selenium Web Driver which will search the element based on the text present on the web element. It looks similar to locator link Text but link text work only when Web element has tag a. So this time we will see another example

<button class="btn btn-info" id="submit" name="submit" style="-webkit-appearance: none; appearance: none; cursor: pointer; font-size: 14px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;">Button</button>

This is a button with text ‘Button’ on it. Xpath can be written for this is

//*[text()='Button']

Here you can add tagName instead of * as well

3. Text() with contains(): For web object type web link we have partial text along with link text. Similarly we have contains method to match partial text as we seen above. This time we will take example of link as it has long text.

<a href="https://github.com/stanfy/behave-rest/blob/master/features/conf.yaml" style="color: #1e73be; margin: 0px; padding: 0px;">Click here to Download File</a>

XPath using this as follows

//a[contains(text(),’Download File’)]

It will identify the elements with partial text.

4. contains(): This function can be used with any attribute by sending partial attribute value. This function is very useful to locate elements with dynamic attribute value

<input name="firstname" style="-webkit-appearance: none; appearance: none; background-color: #fcfcfc; border-color: rgba(173, 176, 182, 0.3); border-radius: 0px; border-style: solid; border-width: 1px; color: #787d85; font-family: verdana, helvetica, arial, verdana, sans-serif; font-size: 13px; height: 38px; line-height: 22px; margin: 0px; outline: 0px; padding: 5px 15px; vertical-align: baseline;" type="text">

We can locate above element using name attribute by sending partial value of it

//input[contains(@name,'first')]

5. Starts-with(): This function identifies web element whose attribute value has some dynamic value in it and element can be identified using starting text of the attribute value. We can identify above element using Starts-with()

//input[starts-with(@name,'first')]

6. Using OR & AND: We can write XPath using conditional expressions using and & or conditions using different attribute. This will be helpful when there are different attributes are changing on different circumstances. These conditional expression should be used very carefully.

Or: There has been 2 attributes with expected values will be send as Xpath expression with OR keyword. This will work if any of the condition returns true.

And: Here also we are passing attributes with expected values as Xpath expression with AND keyword. This will work if both conditions are true and fails even if single condition returns false. We will see what will happen if we use OR and AND on the below element.

<input id="exp-0" name="exp" style="font-size: 14px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding: 0px; vertical-align: baseline;" type="radio" value="1">

In this case if I write expression like this

//input[@id=’exp-0' or @name='exp']

In above expression it searches for element having input tag with either id attribute with value exp=0 or input tag with name attribute having exp. It will return all the elements machetes the above condition. If we check on the given page we have an element with id = exp-0 but we do have 6 radio buttons with name = exp. So in this case it will return 6 elements with this expression. If you use findElement method then it will work on first element or you can use findElements to get the list of web elements.

But you can find this element using AND condition to get unique element if both attributes values are matches.

//input[@id='exp-0' and @name='exp']

Make sure that script should be able to find element when both the attribute values are expected. You can use and condition when you want to find element for element with 2 the attributes with exact expected value and not single attribute with fix value

Above expression will return unique element.

Even we have this much ways to write xpath expressions still it fails to identifies to element when complex or dynamic element. Or element with very common tag like h2 and literally no other attribute attach to the element.

Here Xpath Axes comes to rescue. So what is Xpath Axes?

An XPath axes defines the node-set relative to the current (context) node. It is used to locate the node that is relative to the node on that tree.

What is current context?

A context node can be defined as the node the XPath processor is currently looking at.

XPath axes are methods to identify those dynamic elements which are not possible to find by normal XPath method such as ID, Classname, Name, etc. Axes are so named because they tell about the axis on which elements are lying relative to an element.

These are Xpath axes:

  1. ancestor: The ancestor axes selects all ancestors element (grandparent, parent, etc.) of the current node. We will use same example of radio button. Syntax for using ancestor is like this
//input[@id='exp-0']//ancestor::div[@class='control-group']

Here //input[@id='exp-0'] is current context and we will be finding element by making this element as base.

// Need to add these slash before Xpath axes we are going to use and then we can mention tag after axes name.

ancestor:: This is XPath axes we are using to find element.

div This is the tag name by which we are trying to locate ancestor of current context. Now if you see element till div it will list down all the ancestors of the current context having tag name div. But if we specify further part like complete Xpath it will give you unique ancestor of the current context.

2. ancestor-or-self: The ancestor-or-self axes selects all ancestors (parent, grandparent, etc.) of the current context and the current context itself.

3. child: The child axes selects all children elements of the current context. It helps to identify all the children of context node but If you want to get the specific child element you can add further xpath code to locate unique xpath

We will be using http://automationpractice.com/index.php website to check this. Here top menu has different child element.

//div[@id='block_top_menu']//child::ul

This will return 5 elements as all the child elements are having ul tag. To find out unique element add further details of desired element

4.descendant- The descendant axes selects the descendants (child, grandchild etc) of the current context. We will check descendent for complete page element with input tag.

//div[@id='post-body-3077692503353518311']//descendant::input

It will list down 18 elements. You can pick any element in decedent list.

We will check on this website http://automationpractice.com/index.php for categories menu bar,.

//ul[@class='sf-menu clearfix menu-content sf-js-enabled sf-arrows']/descendant::li

It will return 14 elements

You can further add proper xpath expression to reach your targeted element after li tag with respect to context node.

5. descendant-or-self- The descendant-or-self axes selects the descendants (child, grandchild etc) of the current context and and the current context itself.

//div[@id='post-body-3077692503353518311']//descendant-or-self::input

6. following- The following axes selects each element in the document after the closing tag of the current context with the help of given expression after following::. We have used same expression we used for descendant.

//ul[@class=’sf-menu clearfix menu-content sf-js-enabled sf-arrows’]/following::li

This will return 58 elements. You can specify expression which will return unique element.

7. following-sibling- The following-sibling axes selects all siblings next to current context node. Siblings are at the same level of the current context under same parents. If we try following-sibling on above element we will not get element as category menu bar is last child element of it parent and it has no following sibling

<div id="block_top_menu" class="sf-contener clearfix col-lg-12">
<div class="cat-title">Categories</div>
<ul class="sf-menu clearfix menu-content sf-js-enabled sf-arrows">
<li><a href="http://automationpractice.com/index.php?id_category=3&amp;controller=category" title="Women" class="sf-with-ul">Women</a><ul class="submenu-container clearfix first-in-line-xs" style="display: none;"><li><a href="http://automationpractice.com/index.php?id_category=4&amp;controller=category" title="Tops" class="sf-with-ul">Tops</a><ul style="display: none;"><li><a href="http://automationpractice.com/index.php?id_category=5&amp;controller=category" title="T-shirts">T-shirts</a></li><li><a href="http://automationpractice.com/index.php?id_category=7&amp;controller=category" title="Blouses">Blouses</a></li></ul></li><li><a href="http://automationpractice.com/index.php?id_category=8&amp;controller=category" title="Dresses" class="sf-with-ul">Dresses</a><ul style="display: none;"><li><a href="http://automationpractice.com/index.php?id_category=9&amp;controller=category" title="Casual Dresses">Casual Dresses</a></li><li><a href="http://automationpractice.com/index.php?id_category=10&amp;controller=category" title="Evening Dresses">Evening Dresses</a></li><li><a href="http://automationpractice.com/index.php?id_category=11&amp;controller=category" title="Summer Dresses">Summer Dresses</a></li></ul></li><li id="category-thumbnail"><div><img src="http://automationpractice.com/img/c/3-0_thumb.jpg" alt="Women" title="Women" class="imgm"></div><div><img src="http://automationpractice.com/img/c/3-1_thumb.jpg" alt="Women" title="Women" class="imgm"></div></li></ul></li><li><a href="http://automationpractice.com/index.php?id_category=8&amp;controller=category" title="Dresses" class="sf-with-ul">Dresses</a><ul class="submenu-container clearfix first-in-line-xs" style="display: none;"><li><a href="http://automationpractice.com/index.php?id_category=9&amp;controller=category" title="Casual Dresses">Casual Dresses</a></li><li><a href="http://automationpractice.com/index.php?id_category=10&amp;controller=category" title="Evening Dresses">Evening Dresses</a></li><li><a href="http://automationpractice.com/index.php?id_category=11&amp;controller=category" title="Summer Dresses">Summer Dresses</a></li></ul></li><li><a href="http://automationpractice.com/index.php?id_category=5&amp;controller=category" title="T-shirts">T-shirts</a></li>
</ul>
</div>

you can check for any option inside category menu. Check this xpath in chrome Dev tool

//a[@title='Women']/following-sibling::ul

You can further add proper xpath expression to reach your targeted element after ul tag with respect to context node.

So question comes in to your mind what is difference between descendant and following. why number of elements returned are different. So here descendant will return elements which in hierarchy of current context node like child, grandchild and so on. But Following will scan entire document after current context node with given tag

8. parent- The parent axes selects all siblings next to current context node. We will check for parent of same above element for which we are checking following sibling

//a[@title='Women']/parent::li

You can further add proper xpath expression to reach your targeted element after li tag with respect to context node.

9. preceding- The preceding axes is similar to following, and selects all nodes that appears before the current context in the document, except ancestors. We can check for same context node we checked in following axes. Only difference is we have to check with different tag as there is no li element occurs before context node. So we will be checking number of element with div

//ul[@class=’sf-menu clearfix menu-content sf-js-enabled sf-arrows’]/preceding::div

You can further add proper xpath expression to reach your targeted element after div tag with respect to context node.

10. preceding-sibling- The preceding-sibling axes is similar like following sibling but selects all siblings previous of the current context node. Siblings are at the same level of the current context under same parents. If we try preceding-sibling on above element we will get single unique element with respect to context node.

//ul[@class='sf-menu clearfix menu-content sf-js-enabled sf-arrows']/preceding-sibling::div

So question comes in to your mind what is difference between preceding and ancestor. Why number of elements returned are different. So here ancestor will return elements which in hierarchy of current context node like parent, grandparent and so on. But preceding will scan entire document before current context node with given tag.

11. There is one more axes called self. But I am really not sure what is best case to use this.

Apart from chromeDevTools find method we do in chrome we have another way to validate xpath in Chrome.

  • Right click on page
  • Inspect element
  • Click on Console tab
  • type $x(‘write_your_Xpath’)
  • hit enter
  • it will list down number of elements matches to your Xpath expression
$x("//ul[@class='sf-menu clearfix menu-content sf-js-enabled sf-arrows']/preceding-sibling::div")

This is all about Xpath in Selenium Web Driver.

--

--

Monil Joshi

I am having an 8+ years of experience in software testing. Working with Web, Mobile and API technologies. Visit my GIT https://github.com/monilj