Skip to main content
SeleniumDecoded

XPath Basics

Learn XPath fundamentals for locating elements when CSS selectors aren't enough.

Selenium 3 & 4 Medium

XPath (XML Path Language) is a powerful query language for selecting elements in HTML/XML documents. While CSS selectors are often preferred for their simplicity, XPath offers capabilities that CSS cannot match. For a comprehensive XPath tutorial, check out our sister site XPath Decoded.

When to Use XPath

Use XPath when you need to:

  • Navigate up the DOM tree (to parent elements)
  • Find elements by their text content
  • Use complex conditions combining multiple attributes
  • Navigate sibling relationships

Basic XPath Syntax

ExpressionDescription
//Select from anywhere in document
/Select from root/current element
[@attr]Element with attribute
[@attr='value']Attribute equals value
text()Text content of element
.Current element
..Parent element

Finding Elements by Attribute

XPath Attribute Selectors
Selenium 3 & 4 Stable
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
// By ID (equivalent to By.id())
WebElement byId = driver.findElement(By.xpath("//*[@id='username']"));
// By class (single class)
WebElement byClass = driver.findElement(By.xpath("//*[@class='btn-primary']"));
// By any attribute
WebElement byDataId = driver.findElement(By.xpath("//*[@data-testid='submit']"));
// By attribute containing value
WebElement contains = driver.findElement(
By.xpath("//*[contains(@class, 'btn')]")
);
// By attribute starting with value
WebElement startsWith = driver.findElement(
By.xpath("//*[starts-with(@id, 'user-')]")
);
// Multiple attributes
WebElement multiAttr = driver.findElement(
By.xpath("//input[@type='text'][@name='email']")
);
from selenium.webdriver.common.by import By
# By ID (equivalent to By.ID)
by_id = driver.find_element(By.XPATH, "//*[@id='username']")
# By class (single class)
by_class = driver.find_element(By.XPATH, "//*[@class='btn-primary']")
# By any attribute
by_data_id = driver.find_element(By.XPATH, "//*[@data-testid='submit']")
# By attribute containing value
contains = driver.find_element(
By.XPATH, "//*[contains(@class, 'btn')]"
)
# By attribute starting with value
starts_with = driver.find_element(
By.XPATH, "//*[starts-with(@id, 'user-')]"
)
# Multiple attributes
multi_attr = driver.find_element(
By.XPATH, "//input[@type='text'][@name='email']"
)
const { By } = require('selenium-webdriver');
// By ID (equivalent to By.id())
const byId = await driver.findElement(By.xpath("//*[@id='username']"));
// By class (single class)
const byClass = await driver.findElement(By.xpath("//*[@class='btn-primary']"));
// By any attribute
const byDataId = await driver.findElement(By.xpath("//*[@data-testid='submit']"));
// By attribute containing value
const contains = await driver.findElement(
By.xpath("//*[contains(@class, 'btn')]")
);
// By attribute starting with value
const startsWith = await driver.findElement(
By.xpath("//*[starts-with(@id, 'user-')]")
);
// Multiple attributes
const multiAttr = await driver.findElement(
By.xpath("//input[@type='text'][@name='email']")
);
using OpenQA.Selenium;
// By ID (equivalent to By.Id())
IWebElement byId = driver.FindElement(By.XPath("//*[@id='username']"));
// By class (single class)
IWebElement byClass = driver.FindElement(By.XPath("//*[@class='btn-primary']"));
// By any attribute
IWebElement byDataId = driver.FindElement(By.XPath("//*[@data-testid='submit']"));
// By attribute containing value
IWebElement contains = driver.FindElement(
By.XPath("//*[contains(@class, 'btn')]")
);
// By attribute starting with value
IWebElement startsWith = driver.FindElement(
By.XPath("//*[starts-with(@id, 'user-')]")
);
// Multiple attributes
IWebElement multiAttr = driver.FindElement(
By.XPath("//input[@type='text'][@name='email']")
);

Finding Elements by Text

One of XPath’s unique capabilities is finding elements by their visible text:

XPath Text Selectors
Selenium 3 & 4 Medium
// Exact text match
WebElement exactText = driver.findElement(
By.xpath("//button[text()='Submit']")
);
// Contains text (partial match)
WebElement containsText = driver.findElement(
By.xpath("//button[contains(text(), 'Sub')]")
);
// Normalize whitespace and match
WebElement normalizedText = driver.findElement(
By.xpath("//button[normalize-space(text())='Submit Form']")
);
// Find link by text
WebElement linkByText = driver.findElement(
By.xpath("//a[text()='Click Here']")
);
// Text anywhere in element (including children)
WebElement deepText = driver.findElement(
By.xpath("//div[contains(., 'Welcome')]")
);
# Exact text match
exact_text = driver.find_element(
By.XPATH, "//button[text()='Submit']"
)
# Contains text (partial match)
contains_text = driver.find_element(
By.XPATH, "//button[contains(text(), 'Sub')]"
)
# Normalize whitespace and match
normalized_text = driver.find_element(
By.XPATH, "//button[normalize-space(text())='Submit Form']"
)
# Find link by text
link_by_text = driver.find_element(
By.XPATH, "//a[text()='Click Here']"
)
# Text anywhere in element (including children)
deep_text = driver.find_element(
By.XPATH, "//div[contains(., 'Welcome')]"
)
// Exact text match
const exactText = await driver.findElement(
By.xpath("//button[text()='Submit']")
);
// Contains text (partial match)
const containsText = await driver.findElement(
By.xpath("//button[contains(text(), 'Sub')]")
);
// Normalize whitespace and match
const normalizedText = await driver.findElement(
By.xpath("//button[normalize-space(text())='Submit Form']")
);
// Find link by text
const linkByText = await driver.findElement(
By.xpath("//a[text()='Click Here']")
);
// Text anywhere in element (including children)
const deepText = await driver.findElement(
By.xpath("//div[contains(., 'Welcome')]")
);
// Exact text match
IWebElement exactText = driver.FindElement(
By.XPath("//button[text()='Submit']")
);
// Contains text (partial match)
IWebElement containsText = driver.FindElement(
By.XPath("//button[contains(text(), 'Sub')]")
);
// Normalize whitespace and match
IWebElement normalizedText = driver.FindElement(
By.XPath("//button[normalize-space(text())='Submit Form']")
);
// Find link by text
IWebElement linkByText = driver.FindElement(
By.XPath("//a[text()='Click Here']")
);
// Text anywhere in element (including children)
IWebElement deepText = driver.FindElement(
By.XPath("//div[contains(., 'Welcome')]")
);

XPath excels at navigating relationships between elements:

XPath Navigation
Selenium 3 & 4 Medium
// Parent element
// From <span> find parent <div>
WebElement parent = driver.findElement(
By.xpath("//span[@class='error']/parent::div")
);
// Alternative parent syntax
WebElement parentAlt = driver.findElement(
By.xpath("//span[@class='error']/..")
);
// Ancestor (any level up)
WebElement ancestor = driver.findElement(
By.xpath("//span[@class='error']/ancestor::form")
);
// Child elements
WebElement child = driver.findElement(
By.xpath("//ul[@id='menu']/child::li")
);
// Following sibling
WebElement nextSibling = driver.findElement(
By.xpath("//label[text()='Email']/following-sibling::input")
);
// Preceding sibling
WebElement prevSibling = driver.findElement(
By.xpath("//input[@id='email']/preceding-sibling::label")
);
// Descendant (any level down)
WebElement descendant = driver.findElement(
By.xpath("//div[@id='container']/descendant::button")
);
# Parent element
# From <span> find parent <div>
parent = driver.find_element(
By.XPATH, "//span[@class='error']/parent::div"
)
# Alternative parent syntax
parent_alt = driver.find_element(
By.XPATH, "//span[@class='error']/.."
)
# Ancestor (any level up)
ancestor = driver.find_element(
By.XPATH, "//span[@class='error']/ancestor::form"
)
# Child elements
child = driver.find_element(
By.XPATH, "//ul[@id='menu']/child::li"
)
# Following sibling
next_sibling = driver.find_element(
By.XPATH, "//label[text()='Email']/following-sibling::input"
)
# Preceding sibling
prev_sibling = driver.find_element(
By.XPATH, "//input[@id='email']/preceding-sibling::label"
)
# Descendant (any level down)
descendant = driver.find_element(
By.XPATH, "//div[@id='container']/descendant::button"
)
// Parent element
// From <span> find parent <div>
const parent = await driver.findElement(
By.xpath("//span[@class='error']/parent::div")
);
// Alternative parent syntax
const parentAlt = await driver.findElement(
By.xpath("//span[@class='error']/..")
);
// Ancestor (any level up)
const ancestor = await driver.findElement(
By.xpath("//span[@class='error']/ancestor::form")
);
// Child elements
const child = await driver.findElement(
By.xpath("//ul[@id='menu']/child::li")
);
// Following sibling
const nextSibling = await driver.findElement(
By.xpath("//label[text()='Email']/following-sibling::input")
);
// Preceding sibling
const prevSibling = await driver.findElement(
By.xpath("//input[@id='email']/preceding-sibling::label")
);
// Descendant (any level down)
const descendant = await driver.findElement(
By.xpath("//div[@id='container']/descendant::button")
);
// Parent element
// From <span> find parent <div>
IWebElement parent = driver.FindElement(
By.XPath("//span[@class='error']/parent::div")
);
// Alternative parent syntax
IWebElement parentAlt = driver.FindElement(
By.XPath("//span[@class='error']/..")
);
// Ancestor (any level up)
IWebElement ancestor = driver.FindElement(
By.XPath("//span[@class='error']/ancestor::form")
);
// Child elements
IWebElement child = driver.FindElement(
By.XPath("//ul[@id='menu']/child::li")
);
// Following sibling
IWebElement nextSibling = driver.FindElement(
By.XPath("//label[text()='Email']/following-sibling::input")
);
// Preceding sibling
IWebElement prevSibling = driver.FindElement(
By.XPath("//input[@id='email']/preceding-sibling::label")
);
// Descendant (any level down)
IWebElement descendant = driver.FindElement(
By.XPath("//div[@id='container']/descendant::button")
);

Position-Based Selection

XPath allows selecting elements by their position:

XPath Positional Selection
Selenium 3 & 4 Fragile
// First item (XPath is 1-indexed!)
WebElement first = driver.findElement(By.xpath("//ul/li[1]"));
// Last item
WebElement last = driver.findElement(By.xpath("//ul/li[last()]"));
// Second to last
WebElement secondLast = driver.findElement(By.xpath("//ul/li[last()-1]"));
// First 3 items
List<WebElement> firstThree = driver.findElements(
By.xpath("//ul/li[position() <= 3]")
);
// Odd positioned items
List<WebElement> oddItems = driver.findElements(
By.xpath("//ul/li[position() mod 2 = 1]")
);
// Nth child of specific type
WebElement thirdButton = driver.findElement(
By.xpath("(//button[@class='action'])[3]")
);
# First item (XPath is 1-indexed!)
first = driver.find_element(By.XPATH, "//ul/li[1]")
# Last item
last = driver.find_element(By.XPATH, "//ul/li[last()]")
# Second to last
second_last = driver.find_element(By.XPATH, "//ul/li[last()-1]")
# First 3 items
first_three = driver.find_elements(
By.XPATH, "//ul/li[position() <= 3]"
)
# Odd positioned items
odd_items = driver.find_elements(
By.XPATH, "//ul/li[position() mod 2 = 1]"
)
# Nth child of specific type
third_button = driver.find_element(
By.XPATH, "(//button[@class='action'])[3]"
)
// First item (XPath is 1-indexed!)
const first = await driver.findElement(By.xpath('//ul/li[1]'));
// Last item
const last = await driver.findElement(By.xpath('//ul/li[last()]'));
// Second to last
const secondLast = await driver.findElement(By.xpath('//ul/li[last()-1]'));
// First 3 items
const firstThree = await driver.findElements(
By.xpath('//ul/li[position() <= 3]')
);
// Odd positioned items
const oddItems = await driver.findElements(
By.xpath('//ul/li[position() mod 2 = 1]')
);
// Nth child of specific type
const thirdButton = await driver.findElement(
By.xpath("(//button[@class='action'])[3]")
);
// First item (XPath is 1-indexed!)
IWebElement first = driver.FindElement(By.XPath("//ul/li[1]"));
// Last item
IWebElement last = driver.FindElement(By.XPath("//ul/li[last()]"));
// Second to last
IWebElement secondLast = driver.FindElement(By.XPath("//ul/li[last()-1]"));
// First 3 items
IList<IWebElement> firstThree = driver.FindElements(
By.XPath("//ul/li[position() <= 3]")
);
// Odd positioned items
IList<IWebElement> oddItems = driver.FindElements(
By.XPath("//ul/li[position() mod 2 = 1]")
);
// Nth child of specific type
IWebElement thirdButton = driver.FindElement(
By.XPath("(//button[@class='action'])[3]")
);

Warning: Position-based locators are fragile. Use them only when necessary and prefer more stable alternatives.

XPath Functions

FunctionDescriptionExample
contains()Partial matchcontains(@class, 'btn')
starts-with()Begins withstarts-with(@id, 'item-')
text()Element texttext()='Submit'
normalize-space()Trim whitespacenormalize-space(text())='Hello'
not()Negationnot(@disabled)
and / orBoolean logic@type='text' and @name='email'

XPath vs CSS Selectors

FeatureCSSXPath
PerformanceFasterSlightly slower
Text contentNot supportedSupported
Parent traversalNot supportedSupported
Sibling navigationLimitedFull support
ReadabilityMore readableCan be complex
Browser supportExcellentExcellent

Best Practices

  1. Prefer CSS for simple selections
  2. Use XPath when you need text-based or parent/sibling navigation
  3. Avoid position unless absolutely necessary
  4. Keep paths short - long XPath expressions are fragile
  5. Use contains() for partial class matches instead of exact matches

Next Steps