Javascript get XPath of a node
There's not a unique XPath to a node, so you'll have to decide what's the most appropriate way of constructing a path. Use IDs where available? Numeral position in the document? Position relative to other elements?
See getPathTo()
in this answer for one possible approach.
How do i get the xpath of an element in an X/HTML file
You can extract this functionality from an XPath tool I once wrote:
http://webkitchen.cz/lab/opera/xpath-tool/xpath-tool.js
Edit: here you go:
function getXPath(node) {
var comp, comps = [];
var parent = null;
var xpath = '';
var getPos = function(node) {
var position = 1, curNode;
if (node.nodeType == Node.ATTRIBUTE_NODE) {
return null;
}
for (curNode = node.previousSibling; curNode; curNode = curNode.previousSibling) {
if (curNode.nodeName == node.nodeName) {
++position;
}
}
return position;
}
if (node instanceof Document) {
return '/';
}
for (; node && !(node instanceof Document); node = node.nodeType == Node.ATTRIBUTE_NODE ? node.ownerElement : node.parentNode) {
comp = comps[comps.length] = {};
switch (node.nodeType) {
case Node.TEXT_NODE:
comp.name = 'text()';
break;
case Node.ATTRIBUTE_NODE:
comp.name = '@' + node.nodeName;
break;
case Node.PROCESSING_INSTRUCTION_NODE:
comp.name = 'processing-instruction()';
break;
case Node.COMMENT_NODE:
comp.name = 'comment()';
break;
case Node.ELEMENT_NODE:
comp.name = node.nodeName;
break;
}
comp.position = getPos(node);
}
for (var i = comps.length - 1; i >= 0; i--) {
comp = comps[i];
xpath += '/' + comp.name;
if (comp.position != null) {
xpath += '[' + comp.position + ']';
}
}
return xpath;
}
It might need some changes if you want it to work in IE as well.
Get element's xpath in javascript
There are many ways howto access an element with XPath. For example you can access it by node name or by the value of one of it's attributes or child nodes. So you can not expect that javascript gives you exactly one of them.
But as you have the id, the simplest xpath query to access the element would be:
//*[@id="THE_ID"]
Is there a way to get element by XPath using JavaScript in Selenium WebDriver?
You can use document.evaluate
:
Evaluates an XPath expression string and returns a result of the
specified type if possible.
It is w3-standardized and whole documented: https://developer.mozilla.org/en-US/docs/Web/API/Document.evaluate
function getElementByXpath(path) { return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;}
console.log( getElementByXpath("//html[1]/body[1]/div[1]") );
<div>foo</div>
How to calculate the XPath position of an element using Javascript?
Firebug can do this, and it's open source (BSD) so you can reuse their implementation, which does not require any libraries.
3rd party edit
This is an extract from the linked source above. Just in case the link above will change. Please check the source to benefit from changes and updates or the full featureset provided.
Xpath.getElementXPath = function(element)
{
if (element && element.id)
return '//*[@id="' + element.id + '"]';
else
return Xpath.getElementTreeXPath(element);
};
Above code calls this function.
Attention i added some line-wrapping to avoid horizontal scroll bar
Xpath.getElementTreeXPath = function(element)
{
var paths = []; // Use nodeName (instead of localName)
// so namespace prefix is included (if any).
for (; element && element.nodeType == Node.ELEMENT_NODE;
element = element.parentNode)
{
var index = 0;
var hasFollowingSiblings = false;
for (var sibling = element.previousSibling; sibling;
sibling = sibling.previousSibling)
{
// Ignore document type declaration.
if (sibling.nodeType == Node.DOCUMENT_TYPE_NODE)
continue;
if (sibling.nodeName == element.nodeName)
++index;
}
for (var sibling = element.nextSibling;
sibling && !hasFollowingSiblings;
sibling = sibling.nextSibling)
{
if (sibling.nodeName == element.nodeName)
hasFollowingSiblings = true;
}
var tagName = (element.prefix ? element.prefix + ":" : "")
+ element.localName;
var pathIndex = (index || hasFollowingSiblings ? "["
+ (index + 1) + "]" : "");
paths.splice(0, 0, tagName + pathIndex);
}
return paths.length ? "/" + paths.join("/") : null;
};
How to get Element by XPATH in JavaScript?
Look at document.evaluate()
function getElementByXpath(path) {
return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}
function myFunction() {
let x = getElementByXpath("//html[1]/body[1]/button[1]");
x.style.fontSize = "25px";
x.style.color = "red";
}
<p id="demo">Click the button to change the layout of this paragraph</p>
<button onclick="myFunction()">Click Me!</button>
Using XPath in node.js
This is most likely caused by the default namespace (xmlns="http://www.w3.org/1999/xhtml"
) in your HTML (XHTML).
Looking at the xpath docs, you should be able to bind the namespace to a prefix using useNamespaces
and use the prefix in your xpath (untested)...
var exampleLookup = `//*[@id='System_Console_WriteLine_System_String_System_Object_System_Object_System_Object_']/parent::x:div/following-sibling::x:div/x:pre[position()>1]/x:code[contains(@class,'lang-csharp')]`;
var doc = new dom().parseFromString(rawHtmlString, 'text/html');
var select = xpath.useNamespaces({"x": "http://www.w3.org/1999/xhtml"});
var sampleNodes = xpath.select(exampleLookup,doc);
Instead of binding the namespace to a prefix, you could also use local-name()
in your XPath, but I wouldn't recommend it. This is also covered in the docs.
Example...
//*[@id='System_Console_WriteLine_System_String_System_Object_System_Object_System_Object_']/parent::*[local-name()='div']/following-sibling::*[local-name()='div']/*[local-name()='pre'][position()>1]/*[local-name()='code'][contains(@class,'lang-csharp')]
Node.js XPath example?
The problem is that you're taking the string value of a nodeset, which is by definition the string value of the first node. Iterate over the selected nodes in JavaScript, and only take the string value of each individually rather than call the XPath string()
function on the entire nodeset collectively.
It's also unclear why you'd want a nested loop rather than a single loop over all relativePath
elements, but perhaps that was an artifact of the reduction of your complete program to a posted example. In any case, if you wish to restrict your inner loop to only those relativePath
elements beneath the current content-item
context node, use .//relativePath
instead.
Finally, your XML is not well-formed.
Node.js xmldom / xpath working example
I've fixed the above problems in the example below (and indented the XML to ease reading):
var xpath = require('xpath')
var dom = require('xmldom').DOMParser
var le = `
<content>
<data>
<content-item>
<relativePath>/abc/</relativePath>
<text>abc</text>
<leaf>false</leaf>
<lastModified>2018-10-16</lastModified>
</content-item>
<content-item>
<relativePath>/defghi/</relativePath>
<text>defghi</text>
<leaf>false</leaf>
<lastModified>2018-06-23</lastModified>
</content-item>
<content-item>
<relativePath>/jklmn/</relativePath>
<text>jklmn</text>
<leaf>false</leaf>
<lastModified>2019-02-27</lastModified>
</content-item>
</data>
</content>`;
var doc = new dom().parseFromString(le);
var nodes = xpath.select("//relativePath", doc);
nodes.forEach( (n, i) => {
console.log(n.textContent);
});
Output
/abc/
/defghi/
/jklmn/
as requested.
Related Topics
Regex to Match All Instances Not Inside Quotes
Are Variable Operators Possible
How to Programmatically Click a Link with JavaScript
How to Implement Routereusestrategy Shoulddetach for Specific Routes in Angular 2
How to Enable Cors Nodejs with Express
How to Use JavaScript to Read Local Text File and Read Line by Line
How to Save Image from Canvas with CSS Filters
Reading Cookie Expiration Date
HTML - Attributes VS Properties
Sort an HTML List with JavaScript
What Does the "|" (Single Pipe) Do in JavaScript
Building a Promise Chain Recursively in JavaScript - Memory Considerations
What's the Difference Between & and && in JavaScript
How to Mock an Es6 Module Import Using Jest
Dynamic Keys for Object Literals in JavaScript
How to Get All Selected Values from <Select Multiple=Multiple>