Skip to main content
Staff SDET Reference · XPath 1.0/2.0 · Selenium & Playwright

Ultimate XPath Cheatsheet for Automation

A comprehensive, copy-paste reference guide for QA Automation Engineers. Master all 13 XPath axes, robust text handling with normalize-space(), SVG icon locators, and resilient table traversal patterns.

Author:TesterRank Lead SDET Team
Updated:October 2026 Edition
Coverage:13 Axes • Functions • SVGs • Formulas
Depth:2,500+ Words • Production Formulas

1. XPath Fundamentals & DOM Traversal

XPath (XML Path Language) is a query syntax used to navigate and select nodes from an XML or HTML document. Because the Document Object Model (DOM) of every modern webpage is an hierarchical tree structure of element, attribute, and text nodes, XPath allows you to target any element based on its relationships to other elements in the tree.

Basic XPath Syntax Anatomy

//tagname[@attribute='value']

// : Search anywhere in the document

tagname : HTML tag (e.g., input, button)

[@attr='val'] : Filtering predicate condition

2. Absolute vs Relative XPath

One of the most common beginner mistakes is right-clicking an element in Chrome DevTools and selecting "Copy full XPath". This produces an Absolute XPath, which is extremely fragile.

❌ Absolute XPath (Never Use in Enterprise)

/html/body/div[1]/div[2]/main/form/div[3]/input[1]

Starts from the root /html node with a single forward slash. If a frontend developer adds a banner or wrapping layout div, every index shifts and the test breaks instantly.

✅ Relative XPath (Mandatory)

//input[@data-testid='user-email' or @id='userEmail']

Starts with double forward slash (//). Traverses the entire DOM tree searching directly for elements matching semantic attributes, immune to layout refactoring.

3. The 13 XPath Axes: Complete Engineering Reference

An XPath axis represents a directional relationship to the context node. Mastering axes is what separates junior script-recorders from senior SDETs who can automate complex dynamic grids, financial tables, and nested tree components:

Axis NameSyntax / ShorthandDirection & Definition
selfself::node() / .Selects the current context node itself.
parentparent::tag / ..Selects the immediate parent of the current node.
childchild::tag / tagSelects direct child nodes of the current node.
ancestorancestor::tagSelects all ancestors (parent, grandparent, etc.) up to root.
ancestor-or-selfancestor-or-self::tagSelects all ancestors plus the current context node.
descendantdescendant::tag / //tagSelects all descendants (children, grandchildren, etc.) down the tree.
descendant-or-selfdescendant-or-self::tagSelects all descendants plus the current context node.
followingfollowing::tagSelects everything in the document after the closing tag of current node.
following-siblingfollowing-sibling::tagSelects all sibling nodes at the same level after the current node.
precedingpreceding::tagSelects all nodes in the document before the opening tag of current node.
preceding-siblingpreceding-sibling::tagSelects all sibling nodes at the same level before the current node.
attributeattribute::name / @nameSelects an attribute of the current context node.
namespacenamespace::nameSelects namespace nodes of the context node.

4. XPath Functions: text(), contains() & normalize-space()

Functions allow you to write dynamic predicates that match substrings, trim unpredictable spaces, or count child elements:

1. normalize-space() — The Whitespace Killer

Strips leading and trailing spaces and replaces all internal line breaks/tabs with a single space. Always prefer this over text() when matching rendered text:

HTML/XMLHTML/XML
//button[normalize-space()='Confirm Payment']
2. contains() — Partial Match

Matches when an attribute or text contains a specified token:

HTML/XMLHTML/XML
//div[contains(@class,'toast-success') and contains(.,'Order #')]
3. starts-with() — Dynamic IDs

Matches elements where attributes begin with a fixed prefix followed by dynamic hashes:

HTML/XMLHTML/XML
//input[starts-with(@id,'customer_account_')]

5. Dynamic IDs, SVGs & Shadow DOM Challenges

Modern enterprise web apps introduce DOM complexities that cause standard XPaths to fail. Here is how to conquer them:

Locating SVG Elements

SVG icons do not belong to the standard HTML namespace. Standard //svg lookups fail. Use the local-name() function instead:

HTML/XMLHTML/XML
//*[local-name()='svg']/*[local-name()='path']

Shadow DOM Boundaries

XPath cannot pierce Shadow DOM trees by W3C specification. Use Selenium 4 getShadowRoot() with CSS selectors, or Playwright which pierces Shadow DOM natively.

JavaJava
// Selenium 4: host.getShadowRoot().findElement(By.cssSelector('.btn-inner'));

6. XPath vs CSS Selectors: Tradeoff Matrix

CapabilityCSS SelectorsXPathRecommendation
Execution SpeedFaster (Native Browser C++)Slightly Slower (JavaScript parser)CSS for simple ID/Class matches
Upward Traversal (Parent/Ancestor)Limited (:has() modern only)Full (parent::, ancestor::)Use XPath when navigating up to parent
Match by Visible TextNoYes (text(), contains())Use XPath or Playwright getByRole
Sibling Traversal (Previous)No (Only next sibling +)Yes (preceding-sibling::)Use XPath for preceding siblings

7. 20 Production-Grade XPath Formulas

Copy and adapt these battle-tested XPath patterns for your enterprise automation frameworks:

1. Input field by adjacent text label
HTML/XMLHTML/XML
//label[normalize-space()='Email Address']/following-sibling::input

💡 Works across forms where input ID changes dynamically.

2. Table cell based on matching row header
HTML/XMLHTML/XML
//table[@id='users']//tr[td[text()='John Doe']]/td[4]

💡 Locates the 4th column (e.g. Action / Edit button) for a specific user row.

3. Table button inside specific row
HTML/XMLHTML/XML
//tr[td[contains(.,'ACC-10029')]]//button[contains(text(),'Transfer')]

💡 Targets action button corresponding to unique bank account row.

4. SVG icon locator inside button
HTML/XMLHTML/XML
//button[contains(@class,'search-btn')]//*[local-name()='svg']

💡 Handles XML namespace requirement for inline SVG elements.

5. Dynamic ID with stable prefix
HTML/XMLHTML/XML
//button[starts-with(@id,'user_action_') and contains(@class,'btn-active')]

💡 Matches React/Angular generated elements with changing numeric suffixes.

6. Case-insensitive text search (XPath 2.0 / translate)
HTML/XMLHTML/XML
//button[contains(translate(text(),'LOGIN','login'),'login')]

💡 Matches button regardless of uppercase/lowercase rendering in CSS.

7. Last row in dynamic table
HTML/XMLHTML/XML
//table[@id='transactions']//tbody/tr[last()]

💡 Verifies the most recently appended transaction record.

8. Second to last element
HTML/XMLHTML/XML
//ul[@id='breadcrumbs']/li[last()-1]

💡 Selects parent category in navigation trail.

9. Element with multiple CSS classes
HTML/XMLHTML/XML
//div[contains(concat(' ', normalize-space(@class), ' '), ' active ')]

💡 Bulletproof class matching preventing partial substring false-positives.

10. Active tab in tabbed navigation
HTML/XMLHTML/XML
//div[@role='tablist']//button[@role='tab' and @aria-selected='true']

💡 Accessible role pattern matching currently selected tab panel.

8. Frequently Asked Questions (FAQ)

Why does standard XPath fail on SVG icons in Selenium, and how do you locate them?

SVG elements belong to the XML namespace (http://www.w3.org/2000/svg) rather than standard HTML namespaces. In browsers, querying `//svg` often returns zero elements because the XPath engine searches HTML elements. To locate SVGs reliably, use the `local-name()` or `name()` XPath function: `//*[local-name()='svg']` or `//*[local-name()='svg']/*[local-name()='path']`.

What is the difference between //span[text()="Submit"] and //span[.="Submit"]?

`text()` matches only the direct text node child of the element. If the element contains child tags (e.g., `<span>Sub<b>mit</b></span>` or dynamic spans), `text()` returns multiple text nodes or fails exact comparison. In contrast, `.` refers to the string value of the entire element context, concatenating all descendant text nodes. For nested tags or whitespace resilience, `//span[normalize-space()="Submit"]` is the most robust locator.

Can XPath traverse up the DOM tree from child to parent, and can CSS do this?

Yes, XPath natively traverses upwards in the DOM hierarchy using the `parent::` or `ancestor::` axes (or shorthand `..`). For example, `//input[@id="email"]/parent::div`. Standard CSS selectors historically could not navigate up to parent elements. Although the modern CSS `:has()` pseudo-class allows parent selection in modern browsers, XPath remains the standard across older enterprise test suites.

Why is normalize-space() critical when testing React and Angular web applications?

Modern frontend frameworks frequently format HTML templates with carriage returns, newlines, and trailing spaces when rendering variables (e.g., `\n ₹ 5,000 \n`). An exact match like `//div[text()="₹ 5,000"]` fails because of surrounding whitespace. The `normalize-space()` function strips leading and trailing whitespace and collapses internal multiple spaces into a single space, ensuring resilient text matching.

How do you locate dynamic elements whose IDs contain randomized numbers?

In frameworks like React or Salesforce, elements often generate dynamic IDs on each page render (e.g., `id="btn_submit_98432"`). Never hardcode the numeric suffix. Instead, match static attribute tokens: 1) Prefix match: `//button[starts-with(@id, 'btn_submit_')]`, 2) Substring match: `//button[contains(@id, 'submit')]`, or 3) Relative relationship from a permanent text label: `//label[text()='Password']/following-sibling::input`.

Practice XPath on Real Dynamic UIs

Test your XPath formulas against 22 dynamic web challenges: dynamic tables, shadow DOM, nested iframes, and dynamic timing delays.