XPath can do things CSS can't — find elements by their text content, navigate to parent elements, and find preceding siblings. It's slower than CSS, but sometimes it's the only option. Learn both, use CSS by default, fall back to XPath when needed.
// ABSOLUTE XPath — starts from root. NEVER USE THIS.
// Breaks when ANY element in the path changes.
WebElement bad = driver.findElement(
By.xpath("/html/body/div/div[2]/form/div/input"));
// RELATIVE XPath — starts with // anywhere in the document. USE THIS.
WebElement good = driver.findElement(
By.xpath("//input[@id='userId']"));Never copy-paste absolute XPaths from browser DevTools. They look like /html/body/div[1]/div[2]/form/input — and they WILL break. Always write relative XPaths starting with //.
// By any attribute
driver.findElement(By.xpath("//input[@id='userId']"));
driver.findElement(By.xpath("//input[@name='password']"));
driver.findElement(By.xpath("//*[@data-testid='loginBtn']"));
// //* means "any tag" — use when you don't care about the tag name
// By specific tag + attribute
driver.findElement(By.xpath("//button[@type='submit']"));
driver.findElement(By.xpath(
"//input[@placeholder='Enter your password']"));
driver.findElement(By.xpath("//a[@href='/banking/register']"));This is the main reason you need XPath. CSS selectors cannot match by text content. XPath can.
// Exact text match
driver.findElement(By.xpath("//button[text()='Sign In']"));
driver.findElement(By.xpath(
"//h2[text()='Sign In to Your Account']"));
driver.findElement(By.xpath("//span[text()='In Stock']"));
// Partial text match (contains)
driver.findElement(By.xpath("//h1[contains(text(), 'Welcome')]"));
driver.findElement(By.xpath(
"//button[contains(text(), 'ADD TO CART')]"));
// Attribute starts-with
driver.findElement(By.xpath(
"//*[starts-with(@data-testid, 'product-name')]"));
// Matches: product-name-1, product-name-2...
// Attribute contains
driver.findElement(By.xpath(
"//*[contains(@data-testid, 'filter')]"));
// Matches: cat-filter-electronics, clear-filters...
// Normalize whitespace (trims spaces)
driver.findElement(By.xpath(
"//span[normalize-space(text())='In Stock']"));// AND — both conditions must be true
driver.findElement(By.xpath(
"//input[@type='text' and @name='userId']"));
driver.findElement(By.xpath(
"//button[@type='submit' and contains(text(), 'Sign In')]"));
// OR — either condition
driver.findElement(By.xpath(
"//input[@id='userId' or @id='email']"));
// NOT — negate a condition
driver.findElements(By.xpath("//input[not(@type='hidden')]"));
driver.findElements(By.xpath("//button[not(@disabled)]"));Q: What is the difference between absolute and relative XPath?
A: Absolute XPath starts from the root (html) using a single / — like /html/body/div/form/input. It's extremely fragile because any DOM change breaks it. Relative XPath starts with // and can begin anywhere — like //input[@id="email"]. Always use relative XPath in automation.
Key Point: XPath's killer feature is text matching: //button[text()="Sign In"]. CSS can't do this. Use XPath when you need to find elements by their visible text.