Обновлено/упрощено на основе комментария Матиаса:
Я пытаюсь динамически создать HTML-документ, а затем найти элементы в DOM через XPath.
Что странно, так это то, что созданный документ выглядит правильно, и запрос к нему, например, с помощью document.querySelector('<some el>')
, работает, как и ожидалось.
Однако document.evaluate
всегда возвращает значение null для каждого XPath.
Обновление №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 работает для одного объекта документа, но не для другого.
Любая помощь приветствуется! Очень запутался.
Отличный отзыв, Матиас. Спасибо.
Спасибо, я до сих пор не понимаю, что вы подразумеваете под «выбор XPath работает с одним объектом документа, но не с другим». Если я открою jsfiddle, я увижу Via Document: XPath El: true, Selector El: true
и Via Doc: XPath El: true, Selector El: true
. Не могли бы вы объяснить, что вы ожидали от фактического результата?
Я полагаю, вы используете Firefox, я прав? В Google Chrome и Safari во второй строке выводится false. imgur.com/a/wiPXcXH
Я не совсем уверен, что здесь происходит в браузерах webkit, возможно, им не нравится Document.replaceChild
documentElement
, или, может быть, это потому, что вы устанавливаете какую-то разметку, которая на самом деле недействительна внутри элемента <html>
(например, Doctype должен быть установлен на самом деле снаружи он не может содержать узел <html> и т. д., но в любом случае правильный способ анализа строки как документа — использование DOMпарсер:
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>
Обратите внимание, что если бы вместо замены documentElement
вы сделали установите его innerHTML на один из сгенерированных HTMLElement, это также сработало бы в Chrome, но уже не в Firefox ;-)
Пожалуйста, включите сюда весь соответствующий код, а не только ссылку на скрипку. Также, пожалуйста, скажите, что означает «работает не так, как ожидалось». Если вы подозреваете, что проблема связана с XPath, имеет смысл также напрямую показать HTML-документ, полученный в вашем коде. Спасибо!