Использование DOMXPath для замены узла с сохранением его положения
-
22-07-2019 - |
Вопрос
Итак, прошлой ночью мне пришла в голову небольшая идея создать вспомогательный класс для DOMDOCUMENT, который в некоторой степени имитирует способность jQuery манипулировать DOM строкой на основе HTML или XML.Вместо css-селекторов используется XPath.Например:
$Xml->load($source)
->path('//root/items')
->each(function($Context)
{
echo $Context->nodeValue;
});
Это привело бы к вызову функции обратного вызова на каждом результирующем узле.К сожалению, версия PHP < 5.3.x не поддерживает лямбда-функции или замыкания, поэтому на данный момент я вынужден сделать что-то более похожее на это:
$Xml->load($source)
->path('//root/items')
->walk('printValue', 'param1', 'param2');
На данный момент все работает отлично, и я думаю, что этот проект был бы полезен многим людям, но я застрял с одной из функций.Я пытаюсь имитировать метод 'replace' jQuery.Используя следующий код, я могу выполнить это довольно легко, применив следующий метод:
$Xml->load($source)
->path('//root/items')
->replace($Xml->createElement('foo', 'bar')); // can be an object, string or XPath pattern
Код, лежащий в основе этого метода, является:
public function replace($Content)
{
foreach($this->results as $Element)
{
$Element->parentNode->appendChild($Content->cloneNode(true));
$Element->parentNode->removeChild($Element);
}
return $this;
}
Теперь это работает.Он заменяет каждый результирующий элемент клонированной версией $Content .Проблема в том, что он добавляет их в нижнюю часть списка дочерних элементов родительского узла.Вопрос в том, как мне клонировать этот элемент, чтобы заменить другие элементы, сохраняя при этом исходное положение в DOM?
Я думал о реверс-инжиниринге узла, который я должен был заменить.По сути, копирую значения, атрибуты и имя элемента из $Content, но я не могу изменить фактическое имя элемента целевого элемента.
Отражение может быть возможным, но должен быть более простой способ сделать это.
Кто-нибудь?
Решение
Используйте replaceChild вместо appendChild/removeChild.
Другие советы
Посмотрите, имеет ли $element следующую привязку перед удалением, если это так, сделайте вставку перед этим следующим побратимом, в противном случае просто добавьте.
public function replace($Content)
{
foreach($this->results as $Element)
{
if ($Element->nextSibling) {
$NextSiblingReference = $Element->nextSibling;
$Element->parentNode->insertBefore($Content->cloneNode(true),$NextSiblingReference);
}
else {
$Element->parentNode->appendChild($Content->cloneNode(true));
}
$Element->parentNode->removeChild($Element);
}
return $this;
}
Хотя совершенно непроверенный.
Или как предположил Энтони Джонс Заменить ребенка , большой умф как же я упустил этот момент :)