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.
Top comments (0)