How to calculate the XPath position of an element using Javascript?
A function I use to get an XPath similar to your situation, it uses jQuery:
function getXPath( element )
{
var xpath = '';
for ( ; element && element.nodeType == 1; element = element.parentNode )
{
var id = $(element.parentNode).children(element.tagName).index(element) + 1;
id > 1 ? (id = '[' + id + ']') : (id = '');
xpath = '/' + element.tagName.toLowerCase() + id + xpath;
}
return xpath;
}
Small, powerfull and pure-js function
It returns xpath for the element and elements iterator for xpath.
https://gist.github.com/iimos/e9e96f036a3c174d0bf4
function xpath(el) {
if (typeof el == "string") return document.evaluate(el, document, null, 0, null)
if (!el || el.nodeType != 1) return ''
if (el.id) return "//*[@id='" + el.id + "']"
var sames = [].filter.call(el.parentNode.children, function (x) { return x.tagName == el.tagName })
return xpath(el.parentNode) + '/' + el.tagName.toLowerCase() + (sames.length > 1 ? '['+([].indexOf.call(sames, el)+1)+']' : '')
}
Probably you will need to add a shim for IE8 that don't support the [].filter method: this MDN page gives such code.
Usage
Getting xpath for node:var xp = xpath(elementNode)
Executing xpath:
var iterator = xpath("//h2")
var el = iterator.iterateNext();
while (el) {
// work with element
el = iterator.iterateNext();
}
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;
};