XPath (XML Path Language) is a query language for selecting nodes in XML/HTML documents. Firefox has XPath support both in JavaScript and as a built-in DevTools feature. Here's how to use it.
XPath in Firefox DevTools Console
Firefox DevTools Console has a built-in $x() function that evaluates XPath against the current page:
// Find all h2 elements
$x('//h2')
// Find elements with a specific attribute
$x('//*[@class="highlighted"]')
// Find links containing specific text
$x('//a[contains(text(), "Download")]')
// Find elements by attribute value
$x('//input[@type="email"]')
// Find parent of an element with specific class
$x('//*[@class="child"]/parent::*')
// Find next sibling
$x('//h1/following-sibling::p[1]')
// Count elements
$x('count(//li)') // Returns a number
// Find text content of specific elements
$x('//title/text()') // Array of text nodes
Using XPath in JavaScript
The JavaScript XPath API is verbose but powerful:
// Find all paragraphs (returns XPathResult)
const result = document.evaluate(
'//p', // XPath expression
document, // context node
null, // namespace resolver
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, // result type
null // existing result (null = new)
);
// Iterate through results
for (let i = 0; i < result.snapshotLength; i++) {
const node = result.snapshotItem(i);
console.log(node.textContent);
}
// Single node
const single = document.evaluate(
'//h1[1]',
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
).singleNodeValue;
console.log(single?.textContent);
Result types:
-
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE— Array-like of all matching nodes (most useful) -
XPathResult.FIRST_ORDERED_NODE_TYPE— Just the first match -
XPathResult.NUMBER_TYPE— For count() and similar numeric XPath -
XPathResult.STRING_TYPE— For string expressions -
XPathResult.BOOLEAN_TYPE— For boolean expressions
XPath in DevTools Inspector
Find element with XPath:
Ctrl+F in the Inspector panel → change filter from "selector" to "XPath" → type your XPath.
Copy element's XPath:
In the Inspector, right-click an element → Copy → XPath (or Copy → Full XPath)
The "XPath" option gives the shortest unique path. "Full XPath" gives the absolute path from the document root.
Example output:
//*[@id="main-content"]/p[3] (shortest)
/html/body/div[2]/main/p[3] (full path)
XPath vs CSS Selectors
XPath is more powerful than CSS selectors in some ways:
XPath can (CSS can't):
- Select parent nodes:
//span/parent::div - Select based on text content:
//p[contains(text(), "Firefox")] - Navigate sibling axes:
//h1/following-sibling::*[1] - Use string functions:
normalize-space(),translate(),contains()
CSS can (XPath is awkward):
-
:hover,:focus, pseudo-classes -
~(general sibling) — XPath equivalent is verbose - CSS is generally more readable for standard selectors
Rule of thumb: Use CSS selectors for element selection in code. Use XPath when you need text-based selection or parent/ancestor traversal.
XPath for Web Scraping
When scraping structured data from HTML:
// Extract all article titles and links
const titles = document.evaluate(
'//article//h2/a',
document,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
const articles = [];
for (let i = 0; i < titles.snapshotLength; i++) {
const link = titles.snapshotItem(i);
articles.push({
title: link.textContent.trim(),
href: link.href
});
}
console.table(articles);
Test in console first: Use $x('//article//h2/a') in the DevTools console to verify your XPath before putting it in code.
XML Namespaces in XPath
For XML documents with namespaces (SVG, XHTML, Atom feeds), you need a namespace resolver:
// For SVG documents (namespace: http://www.w3.org/2000/svg)
function nsResolver(prefix) {
const namespaces = {
'svg': 'http://www.w3.org/2000/svg',
'xhtml': 'http://www.w3.org/1999/xhtml'
};
return namespaces[prefix] || null;
}
const svgPaths = document.evaluate(
'//svg:path[@fill="red"]',
document,
nsResolver,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
For HTML documents: Namespaces aren't needed (HTML5 parsers don't require them). Use $x('//path') directly.
Common XPath Patterns
// nth child of type
$x('//ul[1]/li[3]') // Third li in first ul
// Last child
$x('//ul/li[last()]')
// Elements with specific text
$x('//button[text()="Submit"]')
// Elements where attribute starts with
$x('//*[starts-with(@class, "btn")]')
// Elements where attribute contains value in space-separated list
$x('//*[contains(concat(" ", @class, " "), " active ")]')
// (This is the correct way to match a class in a class list)
// Count children
$x('count(//div[@id="list"]/li)')
// Find by position
$x('(//table)[2]//tr') // All rows in the second table
// Conditional
$x('//img[@src and not(@alt)]') // Images missing alt text (accessibility!)
XPath in Selenium/Playwright Tests
XPath is commonly used in automated tests:
Selenium (Python):
# Find element by XPath
element = driver.find_element(By.XPATH, '//button[text()="Login"]')
elements = driver.find_elements(By.XPATH, '//table//tr')
Playwright:
// XPath in Playwright
const button = page.locator('xpath=//button[text()="Login"]');
const rows = page.locator('xpath=//table//tr');
// Or using the direct xpath: prefix
await page.locator('//button[text()="Login"]').click();
Test in DevTools first: Before writing Playwright/Selenium code with XPath, test the expression in the Firefox console with $x(). If it returns the right elements, the same XPath will work in tests.
Firefox XPath Extensions
The standard $x() in Firefox console is equivalent to:
// What $x() does under the hood:
function $x(xpath, context = document) {
const result = context.evaluate(
xpath, context, null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null
);
const nodes = [];
for (let i = 0; i < result.snapshotLength; i++) {
nodes.push(result.snapshotItem(i));
}
return nodes;
}
You can save this to a utility if you need it outside of DevTools.
XPath 2.0 and 3.0
The browser XPath implementation is XPath 1.0 only. XPath 2.0 (with sequences, types, regular expressions) is not supported in browsers.
If you need XPath 2.0 features: use a JavaScript XPath library like fontoxpath which implements XPath 3.1.
For most web development use cases, XPath 1.0 is sufficient.
Top comments (0)