Download XPath Cheat Sheet PDF for Quick References

XPath is the most flexible locator strategy in Selenium. When element IDs are dynamic, class names are shared, or the DOM structure is complex, XPath gives you the precision to target exactly what you need. This cheat sheet covers everything from basic syntax to advanced axes, operators, string functions, and real-world patterns for test automation.

Quick Reference: Jump to a Section

Free XPath Cheat Sheet (PDF)

Download our printable XPath cheat sheet covering syntax, all 13 axes, operators, string functions, dynamic locator patterns, and Selenium usage examples for Python and Java.

Download the XPath Cheat Sheet PDF

XPath Syntax and Path Expressions

XPath (XML Path Language) uses a path-like syntax to identify and navigate nodes in XML and HTML documents. In Selenium, you use XPath to locate WebElements in the DOM.

The standard syntax for an XPath expression in Selenium:

XPath = //tagname[@attribute="value"]

XPath syntax diagram showing tagname, attribute, and value components in an expression

The core path expressions for selecting nodes in an XML or HTML document:

Expression Description
nodename Selects all elements with that node name
/ Selects from the root node
// Selects matching elements anywhere in the document
. Selects the current node
.. Selects the parent of the current node
@ Selects attributes

Common Step Expressions

XPath Description
//div Select all div elements anywhere in the document
//[@id='btn'] Select all elements with ID 'btn'
//div[@name='post'] Select all div elements with name attribute 'post'
//a/text() Get the text content of all anchor tags
//a/@href Get the href attribute value of all anchor tags

Prefix Expressions

Prefix Example Description
// //p[@class='footer'] Select paragraph tag from anywhere in the DOM
/ /a Find all anchor tags from the given node
/ /html/body/div Find all div elements starting from the root element

Absolute vs. Relative XPath

You have two ways to locate a WebElement in the DOM: absolute path and relative path.

Absolute XPath

Absolute XPath specifies the complete path from the root element to the target element. It begins with a single forward slash (/), indicating selection from the document root.

/html/body/div[x]/div[y]/

Example:

/html//div/div/div/div[1]/div/a/img

The major downside: any change to the DOM structure breaks the XPath. Avoid absolute paths in production test scripts.

Relative XPath

Relative XPath locates an element based on its position relative to another element rather than the full document path. It begins with a double forward slash (//).

//tagname[@attribute='value']

Relative XPath is the standard choice for Selenium automation. Changes to the page hierarchy outside the targeted element do not break the locator.

Selecting Nodes

XPath Description
div Selects all div elements
/div Selects div element from the root element
div/tr Selects all tr elements that are direct children of div
//tr Selects all tr elements anywhere in the document
div//tr Selects all tr elements that are descendants of div, at any depth
//@class Selects all class attributes in the document

Predicates and Indexing

Predicates let you target a specific node within a set. They appear in square brackets and return a boolean (true or false). You can combine them with relational and boolean operators.

XPath Description
/div/a[1] Selects the first anchor element within the div
/div/a[last()] Selects the last anchor element within the div
/div/a[last()-1] Selects the second-to-last anchor element within the div
/div/a[position()<3] Selects the first two anchor elements within the div
//a[@class] Selects all anchor elements that have a class attribute
//a[@class='btn'] Selects all anchor elements with class attribute value 'btn'
/div/h1[1]/a Selects all anchors within the first h1 that is a child of div
/div/h1/a[1] Selects the first anchor within each h1 inside the div

Predicate Examples with Attributes

Expression Description
element_name[N] Selects element at position N (1-based index)
empinfo/employee[2] Selects the second employee element under empinfo
empinfo/employee[2]/name Selects the name child of the second employee
empinfo/employee[@id] Selects all employee elements that have an id attribute
empinfo/employee[@id=2] Selects employee elements where id equals 2
empinfo/employee[@id=2][2] Selects the second employee element with id of 2
empinfo/employee[1][@id=1] Selects the first employee element with id of 1
//designation[@discipline and @experience] Selects designation elements that have both attributes
//designation[@discipline or @experience] Selects designation elements that have either attribute

Chaining Order Matters

The order of predicates changes the result. These two expressions are not equivalent:

a[1][@href='/']    // The first anchor, only if it has href='/'
a[@href='/'][1]    // The first anchor that has href='/'

Indexing

XPath Description
//a[1] Selects the first anchor tag
//a[last()] Selects the last anchor tag
//ul/li[2] Selects the second li that is a child of ul
//ul/li[position()=2] Selects the second li that is a child of ul (using position)
//ul/li[position()>1] Selects all li elements that are not the first child of ul

Wildcards and Multi-Path Selection

Wildcard Expressions

Expression Description
* Matches any HTML element
@* Matches any attribute of an element
node() Matches any kind of node
/div/* Selects all child elements of a div
//* Selects all elements in the HTML document
//a[@*] Selects all anchor elements that have any attribute
. Matches the current node context
.. Refers to the parent context node
text() Selects all text node children of the current element

Multi-Path Selection with the Union Operator

Use the | operator to combine multiple XPath expressions into a single query:

XPath Description
//div | //a Selects all div and anchor elements in the document
//div/h1 | //div/a Selects all h1 and anchor elements within a div

XPath Axes

XPath defines 13 axes that describe the relationship between the current context node and other nodes in the document. Axes let you navigate the DOM tree in any direction from any node.

Syntax:

AxisName::nodetest[predicate]

All 13 Axes

Axis Shorthand Description
self . Selects the context node itself
child / Selects direct children of the context node
descendant // Selects all descendants at any depth
descendant-or-self // Selects all descendants plus the context node
parent .. Selects the parent of the context node
ancestor   Selects all ancestors up to the root node
ancestor-or-self   Selects all ancestors plus the context node
attribute @ Selects attributes of the context node
following   Selects all nodes after the context node in document order (excluding attributes and namespaces)
following-sibling   Selects all siblings that follow the context node
preceding   Selects all nodes before the context node in document order (excluding attributes and namespaces)
preceding-sibling   Selects all siblings that precede the context node
namespace   Selects all namespace nodes of the context node

Axis Usage Examples

XPath Description
//name/self::* Selects the name context node itself
child::* Selects all child nodes of the context node
child::node() Selects all child nodes of any type
//employee/descendant::* Selects all descendants of the employee node
//employee/descendant-or-self::* Selects all descendants of employee plus the node itself
//employee/ancestor::* Selects all ancestors of the employee node
//employee/ancestor-or-self::* Selects all ancestors plus the employee node itself
//name/parent::* Selects the parent of the name node
//name/parent::employee Returns the parent if it is an employee element, otherwise nothing
//attribute::id Selects all nodes with an id attribute
//attribute::* Selects all nodes with any attribute
//employee[@id=1]/following::* Selects all nodes after employee with id 1
//employee[@id=1]/following-sibling::* Selects all sibling nodes after employee with id 1
//employee[@id=3]/preceding::* Selects all nodes before employee with id 3
//employee[@id=3]/preceding-sibling::* Selects all sibling nodes before employee with id 3
//name/ancestor-or-self::employee Selects the employee ancestor plus the name node itself

XPath Operators

XPath expressions can return a number, boolean, node-set, or string. The following operators let you manipulate and evaluate these values.

All Operators

Operator Description
| Computes the union of two node-sets
+ Addition
- Subtraction
* Multiplication
div Division
mod Modulus (division remainder)
= Equal
!= Not equal
< Less than
<= Less than or equal to
> Greater than
>= Greater than or equal to
or Logical OR (either condition must be true)
and Logical AND (both conditions must be true)
not() Negates the condition

Boolean Operators

Operator Description
and Both conditions must be satisfied
or At least one condition must be satisfied
not() Condition must not be satisfied

String Functions

String functions are among the most useful XPath tools in Selenium, particularly for locating elements with dynamic or partial attribute values.

Function Description
contains(string1, string2) Returns true if string1 contains string2
starts-with(string1, string2) Returns true if string1 starts with string2
ends-with(string1, string2) Returns true if string1 ends with string2
substring(string, offset, length) Returns a section of string starting at offset for the given length
substring-before(string1, string2) Returns the part of string1 before the first occurrence of string2
substring-after(string1, string2) Returns the part of string1 after the first occurrence of string2
string-length(string) Returns the number of characters in the string
normalize-space(string) Strips leading and trailing whitespace, collapses internal whitespace to single spaces
translate(string1, string2, string3) Replaces characters in string1 that match string2 with the corresponding characters in string3
concat(string1, string2, ...) Concatenates all provided strings
format-number(number, format, locale) Returns a formatted version of number using the format string

Math Functions

Function Description
ceiling(number) Returns the smallest integer greater than or equal to the given number
floor(number) Returns the largest integer less than or equal to the given number
round(decimal) Returns the nearest integer to the given decimal
sum(node-set) Returns the sum of the numeric values of each node in the set

Node Functions

Function Description
node() Selects all kinds of nodes
text() Selects text nodes
name() Returns the name of the current node
position() Returns the position of the current node
last() Returns the position of the last node in the context
comment() Selects comment nodes
processing-instruction() Selects processing instruction nodes

XPath Selectors

Descendant Selectors

CSS Equivalent XPath Description
div //div Select all div elements
div h1 //div//h1 Select all h1 within a div element
ul > li //ul/li Select all li elements that are direct children of ul
div > p > a /div/p/a Select all anchors within paragraph tags inside a div
div > * //div/* Select all direct child elements of div
:root / Select the root element of the DOM
:root > body /body Select the body tag

Attribute Selectors

CSS Equivalent XPath Description
#id //*[@id="id"] Select elements with matching ID
.class //*[@class="class"] Select elements with matching class
a[rel] //a[@rel] Select all anchors with a rel attribute
a[href^='/'] //a[starts-with(@href, '/')] Select anchors with href starting with '/'
a[href$='.txt'] //a[ends-with(@href, '.txt')] Select anchors with href ending with '.txt'
a[rel~='details'] //a[contains(@rel, 'details')] Select anchors whose rel value contains 'details'
input[type="password"] //input[@type="password"] Select all password input fields
a#btn[for="XYZ"] //a[@id="btn"][@for="XYZ"] Select anchor with id 'btn' and for attribute 'XYZ'

Order Selectors

CSS Equivalent XPath Description
ul > li:first-of-type //ul/li[1] Select first li that is a child of ul
ul > li:nth-of-type(2) //ul/li[2] Select second li that is a child of ul
li#id:first-of-type //li[1][@id="id"] Select first li with a specific id value
ul > li:last-of-type //ul/li[last()] Select last li that is a child of ul
a:first-child //*[1][name()="a"] Select the first anchor element that is a first child
a:last-child //*[last()][name()="a"] Select the last anchor element that is a last child

Sibling Selectors

CSS Equivalent XPath Description
h1 ~ ul //h1/following-sibling::ul Select all ul tags that are following siblings of h1
h1 ~ #id //h1/following-sibling::[@id="id"] Select elements with a specific ID that are siblings of h1
h1 + ul //h1/following-sibling::ul[1] Select the first ul sibling immediately following h1

Contextual Selectors

XPath Description
//img All image elements
//img/*[1] First child of each image element
//ul/child::li First child li of ul
//img[1] First image element
//img/*[last()] Last child of each image element
//img[last()] Last image element
//img[last()-1] Second-to-last image element
//ul[*] ul elements that have any children

Other Useful Locator Patterns

XPath Description
//p[not(@id)] Select all paragraph tags without an id attribute
//button[text()="Submit"] Select a button with exact text "Submit"
//button[contains(text(),"pass")] Select a button whose text contains "pass"
//product[@price > 3] Select product elements where price attribute is greater than 3
//ul[*] Select ul elements with any children
//ul[li] Select ul elements that have li children specifically
//a[@name or @href] Select anchors with a name or href attribute
//a | //div Union of all anchor and div elements
//table[count(tr) > 1] Select tables with more than one row
//*[.="t"] Select elements containing exactly the text "t"
//a[contains(text(), "Log Out")] Select anchors whose text contains "Log Out"
//a[not(contains(text(), "Log Out"))] Select anchors whose text does not contain "Log Out"
//a[not(@disabled)] Select all non-disabled anchor elements

Attribute Selector Patterns

XPath Description
//img[@id='myId'] Image element with id equal to 'myId'
//img[@id!='myId'] Image elements with id not equal to 'myId'
//img[@name] Image elements that have a name attribute
//*[contains(@id, 'Id')] Any element where id contains the string 'Id'
//*[starts-with(@id, 'Id')] Any element where id starts with 'Id'
//*[ends-with(@id, 'Id')] Any element where id ends with 'Id'
//*[matches(@id, 'r')] Any element where id matches the regex 'r' (XPath 2.0+)
//*[@id='X' or @name='X'] Elements with id X or name X
//*[@name="N"][@value="v"] Elements with name N and value v
//*[@name="N" and not(@value="v")] Elements with name N but not value v
//input[@type="submit"] Submit input buttons
//section[//h1[@id='hi']] Returns section if it contains an h1 descendant with id 'hi'
//*[@id="TestTable"]//tr[3]//td[2] Cell at row 3, column 2 of a table with id 'TestTable'
//input[@checked] Checked checkboxes or radio buttons
//a[@disabled] All disabled anchor elements

Dynamic XPath Strategies

Dynamically generated IDs are one of the most common problems in real-world Selenium automation. When an element has an ID like input_abc123def that changes on every page load, you cannot use an exact attribute match. These patterns handle dynamic attributes reliably.

Partial Attribute Matching

Pattern Example Use Case
contains(@attr, 'value') //input[contains(@id, 'username')] ID contains a stable substring even if the full value changes
starts-with(@attr, 'prefix') //div[starts-with(@id, 'modal')] ID always starts with the same prefix regardless of dynamic suffix
ends-with(@attr, 'suffix') //input[ends-with(@name, '_field')] Attribute always ends with a known suffix
Combining conditions //input[contains(@id, 'user') and @type='text'] Narrow down results when a partial match is not unique enough

Text-Based Location

Pattern Example Use Case
Exact text match //button[text()='Log In'] Button or link with a known, stable label
Partial text match //button[contains(text(),'Submit')] Label text may have surrounding whitespace or vary slightly
Normalized text //label[normalize-space(text())='First Name'] Text node has inconsistent whitespace in the source

When a target element has no reliable attributes, navigate to it from a stable nearby element:

<!-- Locate a label, then find its associated input -->
//label[text()='Email']/following-sibling::input[1]

<!-- Locate a table row by cell text, then get another cell in the same row -->
//td[text()='John']/parent::tr/td[3]

<!-- Navigate from a section heading to its sibling content -->
//h3[text()='Shipping Address']/following-sibling::div[1]

Using XPath in Selenium

Here is how to use XPath expressions in Selenium WebDriver across the two most common languages.

Python

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get("https://example.com")

# Basic XPath lookup
element = driver.find_element(By.XPATH, "//input[@id='username']")

# Find multiple elements
items = driver.find_elements(By.XPATH, "//ul[@class='results']/li")

# Wait for element before interacting
element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.XPATH, "//button[text()='Submit']"))
)
element.click()

Java

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;

WebDriver driver = new ChromeDriver();
driver.get("https://example.com");

// Basic XPath lookup
WebElement element = driver.findElement(By.xpath("//input[@id='username']"));

// Find multiple elements
List<WebElement> items = driver.findElements(By.xpath("//ul[@class='results']/li"));

// Wait for element before interacting
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement btn = wait.until(
    ExpectedConditions.presenceOfElementLocated(By.xpath("//button[text()='Submit']"))
);
btn.click();

Testing XPath in Browser DevTools

You can validate any XPath expression directly in Chrome or Firefox DevTools without writing a single line of Selenium code.

  1. Open DevTools (F12)
  2. Go to the Elements tab
  3. Press Ctrl+F (Windows/Linux) or Cmd+F (Mac) to open the search bar
  4. Type your XPath expression directly into the search bar
  5. DevTools highlights all matching elements and shows a count

Method 2: Console with $x()

The $x() function in the browser console evaluates an XPath expression and returns all matching elements as an array:

// Run in the browser console (F12 > Console tab)

// Find all buttons with text "Submit"
$x("//button[text()='Submit']")

// Find input fields with dynamic IDs
$x("//input[contains(@id, 'user')]")

// Count matching elements
$x("//li[@class='active']").length

Method 3: Copy XPath from DevTools

  1. Open DevTools (F12)
  2. Click the element picker (top-left cursor icon in the DevTools panel)
  3. Click the element on the page
  4. Right-click the highlighted code in the Elements panel
  5. Select Copy > Copy XPath

Note: the copied XPath is usually an absolute path. Treat it as a starting point and rewrite it as a relative expression before adding it to your test scripts.

XPath vs. CSS Selectors

Both XPath and CSS selectors locate elements, but they have different strengths. Understanding the tradeoffs helps you pick the right tool for each situation.

Feature XPath CSS Selector
Traverse up (parent) Yes: //input/.. or parent::div No (CSS cannot traverse upward)
Select by text content Yes: //a[text()='Home'] No
Partial attribute matching Yes: contains(), starts-with() Yes: [attr*=val], [attr^=val]
Select by index Yes: //li[3] Yes: li:nth-of-type(3)
Axis navigation Full (ancestor, sibling, following, preceding) Limited (next-sibling only)
Readability Verbose, steeper learning curve Concise, familiar to front-end developers
Performance in browsers Slightly slower Faster (browsers are optimized for CSS)
Dynamic ID handling Excellent with contains() Good with attribute substring selectors
Shadow DOM Limited support Better support

Use CSS selectors for straightforward element selection when you know the class, ID, or tag. Use XPath when you need to traverse upward, match by text, use complex axis relationships, or handle dynamic attributes that CSS substring selectors cannot accommodate as cleanly.

Common XPath Mistakes

These are the patterns most likely to cause brittle tests or unexpected failures in real Selenium projects.

1. Using Absolute XPath

Absolute XPath breaks the moment a developer adds or removes a wrapping element anywhere in the path. Always use relative XPath in test scripts.

<!-- Fragile: breaks if any ancestor changes -->
/html/body/div[2]/div[1]/form/input[1]

<!-- Robust: targets the element by its own attributes -->
//input[@name='email']

2. Index-Based Locators Without a Reliable Anchor

XPath like //div[3] depends entirely on DOM order. Adding a feature that inserts a new div above your target breaks the locator silently.

<!-- Fragile: order-dependent -->
//div[3]

<!-- Robust: select by intent -->
//div[@id='checkout-summary']

3. Whitespace Issues with text()

The text() function matches exact text node content. If the HTML has leading, trailing, or extra internal whitespace, an exact match fails.

<!-- Fails if label renders as "  First Name  " -->
//label[text()='First Name']

<!-- Robust: strips whitespace before matching -->
//label[normalize-space(text())='First Name']

4. Matching Class Attributes Exactly

Using @class='btn' fails when the element has multiple classes like class="btn btn-primary".

<!-- Fails for multi-class elements -->
//button[@class='btn']

<!-- Robust: checks if the class list contains 'btn' -->
//button[contains(@class,'btn')]

5. Case Sensitivity

XPath 1.0 (the version used in most browsers) is case-sensitive. The element <INPUT> does not match //input. HTML documents served in standard mode use lowercase tags, but XML documents may vary. Use translate() if you need case-insensitive matching.

//input[translate(@type,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='submit']

XPath with jQuery

jQuery supports a subset of XPath-style expressions. The table below maps common jQuery traversal methods to their XPath equivalents.

jQuery XPath Description
$('ul > li').parent() //ul/li/.. Select all ul elements that are parents of li
$('li').closest('section') //li/ancestor-or-self::section Select the nearest ancestor section of any li
$('p').text() //p/text() Get text content within paragraph tags

What to Learn Next

XPath gives you precision when simpler locators fall short. The patterns that matter most in day-to-day Selenium work are relative paths with contains() and starts-with() for dynamic attributes, axis navigation for reaching elements without direct identifiers, and text-based targeting with normalize-space() for labels and buttons.

Once you are comfortable with XPath, the logical next step is building maintainable locator strategies into a Page Object Model, which keeps your XPath expressions organized and isolated from test logic.

Browse the top-rated Selenium courses on hackr.io

Frequently Asked Questions

What is the difference between absolute and relative XPath in Selenium?

Absolute XPath starts from the document root (beginning with /html) and traces the complete path to the element. Relative XPath starts from anywhere in the document using // and locates an element based on its own attributes or relationship to nearby elements. Use relative XPath in test scripts. Absolute paths break whenever the DOM structure changes, even if the target element itself is unchanged.

When should I use XPath instead of CSS selectors?

Use XPath when you need to: traverse upward to a parent or ancestor element, locate elements by their visible text content, use complex axis navigation (following-sibling, preceding, ancestor), or combine multiple attribute conditions with text matching. Use CSS selectors for straightforward class, ID, or tag selection where performance is a priority, since browsers execute CSS selectors faster than XPath.

How do I find an element by its text using XPath?

Use the text() function for exact matches: //button[text()='Submit']. Use contains(text(), 'value') for partial matches: //a[contains(text(), 'Log In')]. When whitespace is unpredictable, wrap the text node with normalize-space(): //label[normalize-space(text())='Email Address'].

Why does my XPath break when the page layout changes?

The most common cause is using absolute XPath or index-based locators like //div[3]. Both depend on the exact position of elements in the DOM tree. Any layout change upstream of the target breaks the locator. Write relative XPath that targets the element by its own stable attributes (id, name, data attributes) rather than its position.

How do I handle dynamic element IDs with XPath?

Use contains() to match a stable substring within the dynamic ID: //input[contains(@id, 'username')]. If the ID always begins with a known prefix, use starts-with(): //div[starts-with(@id, 'modal-')]. For even more robust targeting, combine a partial attribute match with another stable attribute like type or name.

How do I test an XPath expression in Chrome DevTools without running Selenium?

Open DevTools (F12), go to the Console tab, and use the built-in $x() function. Type $x("//your/xpath/here") and press Enter. The console returns an array of all matching DOM elements. You can also press Ctrl+F in the Elements tab and type your XPath expression directly into the search field.

What does the following-sibling axis do in Selenium XPath?

The following-sibling axis selects all elements at the same level of the DOM tree that come after the context node. It is useful when you know a label or heading but need to target an element next to it. For example, //label[text()='Password']/following-sibling::input[1] finds the first input field that shares a parent with the Password label.

What is a real-world XPath example I can use in Selenium?

Imagine a login form. The email input has a dynamically generated ID like input_9f2a. You can locate it reliably with: //input[contains(@id, 'input') and @type='email']. Or navigate from the visible label: //label[text()='Email']/following-sibling::input[1]. Both approaches survive ID changes and minor DOM restructuring, making your tests less brittle over time.

By Sameeksha Medewar

Sameeksha is a freelance content writer for more than half and a year. She has a hunger to explore and learn new things. She possesses a bachelor's degree in Computer Science.

View all post by the author

Subscribe to our Newsletter for Articles, News, & Jobs.

I accept the Terms and Conditions.

Disclosure: Hackr.io is supported by its audience. When you purchase through links on our site, we may earn an affiliate commission.

In this article

Featured Resources

Learn More

Please login to leave comments