Create a gist now

Instantly share code, notes, and snippets.

getXPathTo / getRootXPathTo

I was looking for two clean, tiny and elegant element-to-xpath functions producing short and complete xpaths like id("main")/ul/li[3] or /html/body/ul/li[3] for a given element, and figured I'd just craft my own.

// get a short, clean XPath path to a given element el, within the current page
getXPathTo = (el) => {
if (!el) return '';
var p = el.parentNode;
if (!p) return '';
var t = el.tagName;
var c = Array.from(p.children).filter(n => n.tagName === t);
var n = c.length === 1 ? '' : '[' + (1 + c.indexOf(el)) + ']';
var path = getXPathTo(p) + '/' + t.toLowerCase() + n;
// see if we can do better (= shorter than, or equal to) with an id selector:
var id = el.id;
if (id && // id exists
id.length + 6 <= path.length && // id was shorter
document.querySelectorAll('#' + id).length === 1) // id was unique in page
return 'id("'+ id +'")';
return path;
};
// get a root-relative XPath to an element
getRootXPathTo = (el) => {
if (!el) return '';
var p = el.parentNode;
if (!p) return '';
var t = el.tagName;
var c = Array.from(p.children).filter(n => n.tagName === t);
var n = c.length === 1 ? '' : '[' + (1 + c.indexOf(el)) + ']';
return getRootXPathTo(p) + '/' + t.toLowerCase() + n;
};
// get a short, clean XPath path to a given element el, within the current page
function getXPathTo(el) {
if (!el) return '';
var p = el.parentNode;
if (!p) return '';
var t = el.tagName;
var c = [].slice.call(p.children).filter(function(n) {
return n.tagName === t;
});
var n = c.length === 1 ? '' : '[' + (1 + c.indexOf(el)) + ']';
var path = getXPathTo(p) + '/' + t.toLowerCase() + n;
// see if we can do better (= shorter than, or equal to) with an id selector:
var id = el.id;
if (id && // id exists
id.length + 6 <= path.length && // id was shorter
document.querySelectorAll('#' + id).length === 1) // id was unique in page
return 'id("'+ id +'")';
return path;
}
// get a root-relative XPath to an element
function getRootXPathTo(el) {
if (!el) return '';
var p = el.parentNode;
if (!p) return '';
var t = el.tagName;
var c = [].slice.call(p.children).filter(function(n) {
return n.tagName === t;
});
var n = c.length === 1 ? '' : '[' + (1 + c.indexOf(el)) + ']';
return getRootXPathTo(p) + '/' + t.toLowerCase() + n;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment