XPath (XML Path Language) is a powerful query language for selecting nodes in an XML/HTML document. While CSS selectors are simpler and more commonly used in web development, XPath is more expressive and is essential for certain use cases: test automation, web scraping, browser extensions, and XML processing. Firefox has excellent XPath support, including in DevTools.
XPath vs. CSS Selectors
CSS selectors are better for:
- Most styling and DOM manipulation tasks
- When you only need to go "down" the tree (parent → child)
- Modern web development with querySelector
XPath is better for:
- Selecting a parent based on its child (CSS can't do this)
- Text content matching
- Complex positional logic (last, position, preceding-sibling)
- More expressive queries in test automation
Basic XPath Syntax
/html/body/div # Absolute path from root //div # Select all divs anywhere //div[@class='container'] # With attribute //div[@id='main'] # By ID //*[@class='btn'] # Any element with class 'btn' //p[1] # First paragraph //p[last()] # Last paragraph //p[position() > 2] # Paragraphs after position 2 XPath in Firefox DevTools
Method 1: Console
Firefox's DevTools console supports $x() (Chrome has it too but Firefox accepts more XPath 1.0 syntax):
// Select all elements matching XPath $x('//div[@class="highlight"]') // Get text content of matching elements $x('//h2/text()') // Find parent of element with specific child $x('//ul[li[@class="active"]]') $x() returns an array of matching nodes, just like $$() for CSS selectors.
Method 2: Inspector search
In the Inspector panel, press Ctrl+F to open the search bar. Firefox's search supports XPath — type an XPath expression starting with / or // and it will search using XPath:
//button[contains(@class, 'primary')] //input[@type='submit'] //*[text()='Submit'] Matches are highlighted and counted in the HTML panel.
Useful XPath Patterns
Text content matching
//h1[text()='Welcome'] # Exact match //p[contains(text(), 'Firefox')] # Contains //a[starts-with(text(), 'Read')] # Starts with Multiple conditions (AND/OR)
//div[@class='card' and @data-type='user'] //button[@type='submit' or @type='button'] Traversing up the tree (what CSS can't do)
//input[@name='email']/.. # Parent of email input //span[@class='error']/../.. # Grandparent of error span //td[text()='Active']/../.. # Row containing 'Active' cell Sibling selection
//h2[text()='Features']/following-sibling::ul[1] # First list after 'Features' heading //li[@class='active']/preceding-sibling::li[1] # Previous sibling of active item Positional
//table/tbody/tr[last()] # Last row //table/tbody/tr[position() <= 5] # First 5 rows (//div[@class='item'])[3] # Third item (note extra parens) XPath in Selenium/Playwright for Test Automation
XPath is widely used in Selenium because CSS can't select by text content or traverse up:
# Selenium (Python) from selenium.webdriver.common.by import By driver.find_element(By.XPATH, '//button[contains(text(), "Submit")]') driver.find_element(By.XPATH, '//label[text()="Email"]/following-sibling::input') // Playwright const button = await page.locator('xpath=//button[contains(text(), "Submit")]'); await button.click(); // Playwright prefers text locators over XPath where possible: const button2 = await page.getByText('Submit'); Playwright's recommendation is to prefer text, role, and accessibility locators over XPath/CSS where possible. But XPath is still useful for:
- Legacy test suites
- Finding elements by complex structural relationships
- Third-party HTML you don't control
XPath in JavaScript
The native DOM API has full XPath support:
// document.evaluate(expression, contextNode, namespaceResolver, resultType, result) // Single result const result = document.evaluate( '//h1[contains(text(), "Firefox")]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ); const element = result.singleNodeValue; // Multiple results const results = document.evaluate( '//a[@href]', document, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null ); let node; while ((node = results.iterateNext())) { console.log(node.href); } The $x() shorthand in DevTools is just a wrapper around document.evaluate().
XPath in Firefox Extensions (WebExtensions)
Browser extensions can use XPath via content scripts:
// content script function findByXPath(xpath) { return document.evaluate( xpath, document, null, XPathResult.ORDERED_NODE_ARRAY_TYPE, null ).singleNodeValue; } Useful for extensions that inject UI relative to specific page elements, or for content manipulation extensions.
XPath Namespaces in SVG/XML
For SVG or custom XML namespaces:
function nsResolver(prefix) { const namespaces = { 'svg': 'http://www.w3.org/2000/svg', 'xhtml': 'http://www.w3.org/1999/xhtml' }; return namespaces[prefix] || null; } document.evaluate( '//svg:circle[@r > 10]', document, nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null ); Common XPath Gotchas
Class attributes: XPath's @class='btn' requires an exact match. For partial class matching, use contains(@class, 'btn').
XPath 1.0 only: browsers implement XPath 1.0, which lacks some features from XPath 2.0/3.0 (like sequence functions). $x() in DevTools uses 1.0.
Performance: complex XPath expressions that traverse the entire document can be slow on large DOMs. Prefer specificity (start from a specific context node, not // from the root when possible).
HTML case sensitivity: HTML attributes are case-insensitive in HTML but XPath is case-sensitive. @CLASS and @class are different. Test @class, not @CLASS.

![Why Object.keys(user) Returns string[], Not (keyof User)[]: TypeScript Means It](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2q4w48b9bzs43tynyhsf.png)