Оценка XPath для документа, созданного динамически, не работает

Обновлено/упрощено на основе комментария Матиаса:

Я пытаюсь динамически создать 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, имеет смысл также напрямую показать HTML-документ, полученный в вашем коде. Спасибо!

Mathias Müller 21.06.2019 09:24

Отличный отзыв, Матиас. Спасибо.

haberdasher 22.06.2019 01:12

Спасибо, я до сих пор не понимаю, что вы подразумеваете под «выбор XPath работает с одним объектом документа, но не с другим». Если я открою jsfiddle, я увижу Via Document: XPath El: true, Selector El: true и Via Doc: XPath El: true, Selector El: true. Не могли бы вы объяснить, что вы ожидали от фактического результата?

Mathias Müller 22.06.2019 10:39

Я полагаю, вы используете Firefox, я прав? В Google Chrome и Safari во второй строке выводится false. imgur.com/a/wiPXcXH

haberdasher 24.06.2019 03:03
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
4
152
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я не совсем уверен, что здесь происходит в браузерах webkit, возможно, им не нравится Document.replaceChilddocumentElement, или, может быть, это потому, что вы устанавливаете какую-то разметку, которая на самом деле недействительна внутри элемента <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 ;-)

Другие вопросы по теме