Есть ли в любом случае, чтобы вернуть строку XPath элемента DOM в Javascript?Javascript получить XPath узла
ответ
Не существует уникального XPath для узла, поэтому вам придется решить, что является наиболее подходящим способом построения пути. Использовать идентификаторы, если они доступны? Позиция позиции в документе? Позиция относительно других элементов?
См. getPathTo()
в this answer для одного возможного подхода.
Я реорганизовал это из другого примера. Он попытается проверить или, безусловно, уникальный идентификатор, и если это так, используйте этот случай, чтобы сократить выражение.
function createXPathFromElement(elm) {
var allNodes = document.getElementsByTagName('*');
for (var segs = []; elm && elm.nodeType == 1; elm = elm.parentNode)
{
if (elm.hasAttribute('id')) {
var uniqueIdCount = 0;
for (var n=0;n < allNodes.length;n++) {
if (allNodes[n].hasAttribute('id') && allNodes[n].id == elm.id) uniqueIdCount++;
if (uniqueIdCount > 1) break;
};
if (uniqueIdCount == 1) {
segs.unshift('id("' + elm.getAttribute('id') + '")');
return segs.join('/');
} else {
segs.unshift(elm.localName.toLowerCase() + '[@id="' + elm.getAttribute('id') + '"]');
}
} else if (elm.hasAttribute('class')) {
segs.unshift(elm.localName.toLowerCase() + '[@class="' + elm.getAttribute('class') + '"]');
} else {
for (i = 1, sib = elm.previousSibling; sib; sib = sib.previousSibling) {
if (sib.localName == elm.localName) i++; };
segs.unshift(elm.localName.toLowerCase() + '[' + i + ']');
};
};
return segs.length ? '/' + segs.join('/') : null;
};
function lookupElementByXPath(path) {
var evaluator = new XPathEvaluator();
var result = evaluator.evaluate(path, document.documentElement, null,XPathResult.FIRST_ORDERED_NODE_TYPE, null);
return result.singleNodeValue;
}
Протестировали XPaths эти продукты, используя объекты DOMDocument и DOMXPath от PHP - они, похоже, работают очень хорошо. – tonyhb
Это замечательно! Я искал что-то вроде этого какое-то время, и это действительно самое полное решение, которое я видел. У тебя есть +1. Благодаря! –
'segs' становится глобальной переменной здесь. – mattsven
Аналогичное решение задается функцией getXPathForElement на MDN:
function getXPathForElement(el, xml) {
var xpath = '';
var pos, tempitem2;
while(el !== xml.documentElement) {
pos = 0;
tempitem2 = el;
while(tempitem2) {
if (tempitem2.nodeType === 1 && tempitem2.nodeName === el.nodeName) { // If it is ELEMENT_NODE of the same name
pos += 1;
}
tempitem2 = tempitem2.previousSibling;
}
xpath = "*[name()='"+el.nodeName+"' and namespace-uri()='"+(el.namespaceURI===null?'':el.namespaceURI)+"']["+pos+']'+'/'+xpath;
el = el.parentNode;
}
xpath = '/*'+"[name()='"+xml.documentElement.nodeName+"' and namespace-uri()='"+(el.namespaceURI===null?'':el.namespaceURI)+"']"+'/'+xpath;
xpath = xpath.replace(/\/$/, '');
return xpath;
}
XMLSerializer Также может быть стоит попробовать.
Ссылка getXPathForElement не ссылается на функцию getXPathForElement? – TheBlastOne
Вот функциональное программирование стиля функция ES6 для работы:
function getXPathForElement(element) {
const idx = (sib, name) => sib
? idx(sib.previousElementSibling, name||sib.localName) + (sib.localName == name)
: 1;
const segs = elm => !elm || elm.nodeType !== 1
? ['']
: elm.id && document.querySelector(`#${elm.id}`) === elm
? [`id("${elm.id}")`]
: [...segs(elm.parentNode), `${elm.localName.toLowerCase()}[${idx(elm)}]`];
return segs(element).join('/');
}
function getElementByXPath(path) {
return (new XPathEvaluator())
.evaluate(path, document.documentElement, null,
XPathResult.FIRST_ORDERED_NODE_TYPE, null)
.singleNodeValue;
}
// Demo:
const li = document.querySelector('li:nth-child(2)');
const path = getXPathForElement(li);
console.log(path);
console.log(li === getElementByXPath(path)); // true
<div>
<table id="start"></table>
<div>
<ul><li>option</ul></ul>
<span>title</span>
<ul>
<li>abc</li>
<li>select this</li>
</ul>
</div>
</div>
будет использовать селектор id
, если элемент не является первым с этим идентификатором. Селекторы классов не используются, потому что в интерактивных веб-страницах классы могут часто меняться.
function getElementXPath (element) {
if (!element) return null
if (element.id) {
return `//*[@id=${element.id}]`
} else if (element.tagName === 'BODY') {
return '/html/body'
} else {
const sameTagSiblings = Array.from(element.parentNode.childNodes)
.filter(e => e.nodeName === element.nodeName)
const idx = sameTagSiblings.indexOf(element)
return getElementXPath(element.parentNode) +
'/' +
element.tagName.toLowerCase() +
(sameTagSiblings.length > 1 ? `[${idx + 1}]` : '')
}
}
console.log(getElementXPath(document.querySelector('#a div')))
<div id="a">
<div>def</div>
</div>
Вам следует добавить объяснение в свой ответ. – Sand
Эй, спасибо, что выглядит как хорошая функция. Я сделал другой вопрос более подходящим и более контекстом: http://stackoverflow.com/questions/2661918/javascript-crazy-idea-finding-a-node. В ретроспективе я должен был отредактировать этот ... oops lol. – Louis
+1 «Не существует уникального XPath для узла» (и возможных альтернатив). – dakab
XPath определен как путь к узлу из корня документа. –