We can write dynamic XPath using any of the following technique:

  • starts-with()
  • contains()
  • text()
  • following
  • following-sibling
  • child
  • Index
  • AND Operator
  • OR Operator

Let us now understand these techniques in details with the help of below DOM structure:

1. Locating using starts-with():

We can use this method to find an element when its attribute value is starting with the given string/pattern.

This approach is used when the attribute’s trailing value is changing at runtime but the initial part of the value is fixed.

Examples: test_1, test_30, test_pqr.

Syntax:

  • .//*[starts-with(@attributeName,'value')]
  • .//tagName[starts-with(@attributeName,'value')]

XPath to find all <input> tags with attribute name starting with string ‘txt’ can be written as:

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

2. Locating using contains():

We can use this method to find an element when its attribute value is containing the given string/pattern.

This approach is used when the attribute’s value is changing at runtime but some part of the value is fixed.

Example: 1_Password_abc, 2_Password_pqr, 3_Password_lmn.

Syntax:

  • .//*[contains(@attributeName,'value')]
  • .//tagName[contains(@attributeName,'value')]

XPath to find all elements containing text ‘Password’ in its id attribute can be written as:

.//*[contains(@id,'Password')]

3. Locating using text():

We can use this method when all attributes of an element are dynamic but it has a static text value.

Examples:

Syntax:

  • .//*[text()='value']
  • .//tagName[text()='value']

XPath to locate ‘Forgot your password?’ link can be written as:

.//a[text()='Forgot your password?']

4. Locating using following:

This type is used when we want to locate a child element w.r.t parent element reference.

Syntax:

.//parentElement//following::childElement

We can write below statement to find all <input> tags present inside <form> tag.

.//form[@id='frmLogin']//following::input

5. Locating using following-sibling:

This type is used to find siblings of currently selected element.

Suppose we are unable to locate the Password <div> directly from above DOM structure but we can locate the Username <div>. Then, in such a case, we use the following-sibling approach.

.//div[@id='username']//following-sibling::div

With this statement, we will get two matching nodes. One for Password div and another for the Login button div.

Syntax:

.//currentElement//following-sibling::siblingElement

6. Locating using child:

This type is used to find all the child nodes from the parent element.

Example:

We want to find how many hobbies checkboxes are present under the hobbies section on a form. In such a case, we can simply locate the hobbies section and find all the child elements with <input> type.

Syntax:

  • Direct child: .//parentElement/child::tagName
  • All child: .//parentElement//child::tagName

To locate only the direct child <div> (Username, Password, and Login Button) present under <form> tag:

.//form[@id='frmLogin']/child::div

To locate all the <div> (including Forgot Password Link) present inside <form> tag:

.//form[@id='frmLogin']//child::div

7. Locating using Index:

We use Indexing when we are not able to write the generic path to locate a specific element.

Indexing an element is not recommended but if there is no other alternative then only use this approach as we are hardcoding element path while locating it.

Suppose we get a DOM structure like below where the only element we can locate is <form> and all child elements do not have any attributes.

Note: This is a rare case scenario. I’m just using it to make the concept easier to understand. If we find such a DOM structure in our organization then we need to speak with our dev team and request them to add attributes to the elements.

I have added index to each <div> in comments to refer.

Now, If we want to locate all <div> present under <form> then we can write the path as: .//form[@id='frmLogin']/div

This path will locate div1, div2, and div3 as we are writing /div and not //div.

To locate only the Username <div>, we can modify the path as: .//form[@id='frmLogin']/div[1]

When we write div[1], it means we are expecting a username <div> to be at the first index inside <form>. If the developer adds another <div> before the username <div> then the newly added element will be selected.

So it is risky to locate elements with indexing and if DOM structure changes frequently then we need to keep modifying our XPath on a frequent basis in order for our check to succeed.

8. Locating using AND Operator:

AND Operator is used when we want to locate an element satisfying all the conditions.

Syntax:

  • .//*[condition1 and condition2]
  • .//tagName[condition1 and condition2]

Statement to locate the Username field with multiple conditions:

.//input[@name='txtUsername' and @type='text']

We can use starts-with(), contains(), and text() methods as a part of the condition statement:

.//input[starts-with(@id,'txt') and @name='txtUsername']

9. Locating using OR Operator:

OR Operator is used when we want to locate an element satisfying any one of the conditions.

Syntax:

  • .//*[condition1 or condition2]
  • .//tagName[condition1 or condition2]

Example:

.//input[starts-with(@id,'txt') or @name='txtUsername']

This statement will locate both the username and the password field as the first condition is valid for both the elements.