How to Get an Element by XPath in JavaScript
While CSS selectors are the standard for querying the DOM in modern web development, there are specific situations where you might need to select an element using an XPath expression. This is common in testing environments or for complex queries that are difficult to express with CSS, such as selecting a parent element.
This guide will first show you the modern and recommended alternative to most XPath use cases—querySelector. Then, it will teach you how to use the native document.evaluate() method to execute true XPath queries and explain when you should choose one over the other.
For the examples below we will use the following HTML code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>tutorialreference.com</title>
</head>
<body>
<div>
<p>Hello world</p>
</div>
<div>
<p>Apple, Banana, Kiwi</p>
</div>
<script src="index.js"></script>
</body>
</html>
The Modern Alternative (Best Practice): CSS Selectors
For at least 95% of use cases, a CSS selector used with querySelector() or querySelectorAll() is simpler, more readable, and better supported than XPath. Many simple XPath expressions have a direct CSS equivalent.
XPath vs. CSS Selector
-
XPath:
//p(selects all<p>elements) -
CSS Selector:
p -
XPath:
/html/body/div[2]/p(selects a<p>in the second<div>of the body) -
CSS Selector:
body > div:nth-of-type(2) > p
Unless you have a specific need for XPath's advanced features, you should always prefer querySelector.
// This is the standard, modern way to select elements.
const paragraph = document.querySelector('body > div:nth-of-type(2) > p');
The XPath Method: document.evaluate()
For true XPath queries, you must use the document.evaluate() method. This is a powerful but verbose API.
Syntax:
document.evaluate(xpathExpression, contextNode, namespaceResolver, resultType, result)
For our purposes, we only need to focus on a few key parameters:
xpathExpression: The XPath string you want to execute.contextNode: The part of the document to search within (usually justdocument).resultType: A constant that specifies what kind of result you want.
Solution: Getting a Single Element
To get the first matching element, you use XPathResult.FIRST_ORDERED_NODE_TYPE.
const xpath = '/html/body/div[2]/p';
const result = document.evaluate(
xpath,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
);
const element = result.singleNodeValue;
console.log(element); // Output: <p>Apple, Banana, Kiwi</p>
Solution: Getting Multiple Elements
To get an iterable collection of all matching elements, you use XPathResult.ORDERED_NODE_ITERATOR_TYPE.
const xpath = '//p'; // Get all <p> elements
const result = document.evaluate(
xpath,
document,
null,
XPathResult.ORDERED_NODE_ITERATOR_TYPE,
null
);
// The result is an iterator, not an array. We must loop through it.
let currentNode = result.iterateNext();
while (currentNode) {
console.log(currentNode.textContent);
currentNode = result.iterateNext();
}
Output:
"Hello world"
"Apple, Banana, Kiwi"
Creating Reusable Helper Functions for XPath (Recommended)
Because the document.evaluate() API is so cumbersome, the best practice is to wrap it in simple, reusable helper functions.
The Reusable Functions
function getElementByXPath(xpath) {
return document.evaluate(
xpath,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
).singleNodeValue;
}
function getAllElementsByXPath(xpath) {
const result = document.evaluate(
xpath,
document,
null,
XPathResult.ORDERED_NODE_ITERATOR_TYPE,
null
);
const elements = [];
let currentNode = result.iterateNext();
while (currentNode) {
elements.push(currentNode);
currentNode = result.iterateNext();
}
return elements;
}
The Solution (Using Helpers)
With these helpers, your code becomes much cleaner and more readable.
// Get a single element
const firstP = getElementByXPath('/html/body/div[2]/p');
console.log(firstP.textContent); // Output: "Apple, Banana, Kiwi"
// Get all elements
const allParagraphs = getAllElementsByXPath('//p');
allParagraphs.forEach(p => console.log(p.textContent));
When to Use XPath vs. CSS Selectors
While CSS selectors are generally better, XPath has a few unique and powerful features.
You should use XPath when you need to:
- Select a Parent or Ancestor: This is XPath's killer feature. CSS selectors can only traverse down the DOM tree, but XPath can go up using
..(parent) or theancestor::axis.// Find the parent <div> of a specific paragraph
const parentDiv = getElementByXPath("//p[text()='Apple, Banana, Kiwi']/.."); - Select Elements Based on Their Text Content: XPath can directly query the text inside an element, which is something CSS selectors cannot do.
// Find the button that contains the text "Submit"
const submitButton = getElementByXPath("//button[contains(text(), 'Submit')]");
Conclusion
While both XPath and CSS selectors can query the DOM, they serve different purposes in modern web development.
- For 99% of use cases, you should use
document.querySelector()anddocument.querySelectorAll()with CSS selectors. They are the web standard, they are more readable, and they are highly optimized in modern browsers. - Reserve the use of
document.evaluate()with XPath for its few unique and powerful features, such as selecting parent elements or querying based on text content. When you do use it, wrap it in a helper function to keep your code clean.