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.