动态创建的文档的XPath评估无法正常工作

时间:2019-06-21 05:00:30

标签: javascript dom xpath

根据Mathias的评论进行了更新/简化:

我试图动态创建HTML文档,然后通过XPath在DOM中查找元素。

奇怪的是,所创建的文档看起来可以正确构建,并且可以使用document.querySelector('<some el>')查询它,例如可以正常工作。

但是,document.evaluate总是为每个XPath返回null。

更新#2:对于Chrome + Safari来说确实如此。一切都能在Firefox中正常运行。

function createDocumentFromHTMLContent(htmlContent) {
  const htmlEl = document.createElement('HTML');
  htmlEl.innerHTML = htmlContent;

  const doctype = document.implementation.createDocumentType('html', '', '');
  const doc = document.implementation.createDocument('', 'html', doctype);
  doc.replaceChild(htmlEl, doc.firstElementChild);
  return doc;
}

function getElementByXpath(path, doc) {
  doc = doc || document;
  return doc.evaluate(path, doc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}

const pageContent = `
<!DOCTYPE html>
<html>
<head>
  <title>Yup</title>
</head>
<body>
  <h1>Title</h1>
</body>
</html>
`;

const doc = createDocumentFromHTMLContent(pageContent);
const xpath = '/html[1]/body[1]/h1';
const onDoc = {
  viaXPath: getElementByXpath(xpath, doc),
  viaSelector: doc.querySelector('h1'),
};

const onDocument = {
  viaXPath: getElementByXpath(xpath, document),
  viaSelector: document.querySelector('h1'),
};

const summarize = (obj) => `XPath El: ${!!obj.viaXPath}, Selector El: ${!!obj.viaSelector}`;

const summaryEl = document.createElement('p');
summaryEl.innerHTML = `Via Document: ${summarize(onDocument)}<br />Via Doc: ${summarize(onDoc)}`;
document.body.appendChild(summaryEl);

这是JSFiddle中的上述内容:https://jsfiddle.net/two2hg0z/

我不知道为什么XPath选择可以在一个文档对象上起作用,而不能在另一个文档对象上起作用。

感谢您的帮助!很沮丧。

1 个答案:

答案 0 :(得分:2)

我不太确定Webkit浏览器在这里发生了什么,可能他们不喜欢Document.replaceChild documentElement,或者可能是因为您设置的标记实际上在<html>元素(例如Doctype实际上应设置在外部,它不能包含节点等。但是无论如何,将字符串解析为Document的正确方法是通过使用DOMParser

function createDocumentFromHTMLContent(htmlContent) {
  return new DOMParser().parseFromString(htmlContent, 'text/html');
}

function getElementByXpath(path, doc) {
  doc = doc || document;
  return doc.evaluate(path, doc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}

const pageContent = `
<!DOCTYPE html>

<html>
<head>
  <title>Yup</title>
</head>
<body>
  <h1>Title</h1>
</body>
</html>
`;
const doc = createDocumentFromHTMLContent(pageContent);

const xpath = '/html[1]/body[1]/h1';
const onDoc = {
  viaXPath: getElementByXpath(xpath, doc),
  viaSelector: doc.querySelector('h1'),
};

const onDocument = {
  viaXPath: getElementByXpath(xpath, document),
  viaSelector: document.querySelector('h1'),
};

const summarize = (obj) => `XPath El: ${!!obj.viaXPath}, Selector El: ${!!obj.viaSelector}`;

const summaryEl = document.createElement('p');
summaryEl.innerHTML = `Via Document: ${summarize(onDocument)}<br />Via Doc: ${summarize(onDoc)}`;
document.body.appendChild(summaryEl);
<h1>Title</h1>

请注意,如果您执行set its innerHTML to the one of your generated HTMLElement而不是替换documentElement,那么它也可以在Chrome浏览器中工作,但在Firefox中不再可用;-)