我有一个可怕的算法,“删除节点”,将其内部内容移动到其父节点(见下文)……但我认为有可能使用
DOMDocumentFragment(而不是使用saveXML / loadXML)开发更好的算法.
下面的算法灵感来自renameNode().
/** * Move the content of the $from node to its parent node. * Conditions: parent not a document root,$from not a text node. * @param DOMElement $from to be removed,preserving its contents. * @return true if changed,false if not. */ function moveInner($from) { $to = $from->parentNode; if ($from->nodeType==1 && $to->parentNode->nodeType==1) { // Scans $from,and record information: $lst = array(); // to avoid "scan bugs" of DomNodeList iterator foreach ($to->childNodes as $e) $lst[] = array($e); for($i=0; $i<count($lst); $i++) if ($lst[$i][0]->nodeType==1 && $from->isSameNode($lst[$i][0])) { $lst[$i][1] = array(); foreach ($lst[$i][0]->childNodes as $e) $lst[$i][1][] = $e; } // Build $newTo (rebuilds the parent node): $newTo = $from->ownerDocument->createElement($to->nodeName); foreach ($to->attributes as $a) { $newTo->setAttribute($a->nodeName,$a->nodeValue); } foreach ($lst as $r) { if (count($r)==1) $newTo->appendChild($r[0]); else foreach ($r[1] as $e) $newTo->appendChild($e); } // Replaces it: $to->parentNode->replaceChild($newTo,$to); return true; } else return false; }
例
INPUT
<html id="root"> <p id="p1"><i>Title</i></p> <p id="p2"><b id="b1">Rosangela<sup>1</sup>,Maria<sup>2</sup></b>,<b>Eduardo<sup>4</sup></b> </p> </html>
moveInner的输出($dom-> getElementById(‘p1’))
... <p id="p1">Title</p> ...
moveInner的输出($dom-> getElementById(‘b1’))
... <p id="p2">Rosangela<sup>1</sup>,Maria<sup>2</sup>,<b>Eduardo<sup>4</sup></b> </p> ...
首次使用后,moveInner($dom-> getElementById(‘root’))或moveInner($dom-> getElementById(‘p1’))没有变化.
PS:就像一个“TRIM TAG”功能.
当你在同一个文档中移动时,实际上这并不是那么麻烦.您单独发布的代码已经有很多可以自行优化的地方,例如将childNodes NodeList转换为数组只需使用
原文链接:https://www.f2er.com/php/137112.htmliterator_to_array
:
$children = iterator_to_array($from->childNodes);
此外,您应该使用更多的说话变量名称,使用更长的名称没有问题.它只是使代码更具可读性,并留出更快速查看更重要的东西的空间:
/** * Move the content of the $from node to its parent node. * * @param DOMElement $from to be removed,preserving its contents. * @return DOMElement the element removed (w/o it's former children) * @throws InvalidArgumentException in case there is no parent element */ function moveInner(DOMElement $from) { if (!$from->parentNode instanceof DOMElement) { throw new InvalidArgumentException( 'DOMElement does not have a parent DOMElement node.' ); } /** @var DOMNode[] $children */ $children = iterator_to_array($from->childNodes); foreach ($children as $child) { $from->parentNode->insertBefore($child,$from); } return $from->parentNode->removeChild($from); }
它只是有效.如果将相同的元素插入DOMDocument中的另一个位置,则该元素将被移动,而不是重复.
如果要复制(因此要保留子节点而不是移动子节点),可以将子节点用作原型并将其克隆.由于此函数返回已删除的元素,因此它包含副本.
首先是没有克隆的例子,只是上面的函数:
$removed = moveInner($doc->getElementById('b1')); echo $doc->saveHTML(),"\nRemoved: ",$doc->saveHTML($removed);
输出:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <html id="root"><body><p id="p1"><i>Title <b>2</b></i></p> <p id="p2">Rosangela<sup>1</sup>,<b>Eduardo<sup>4</sup></b> </p> </body></html> Removed: <b id="b1"></b>
$from->parentNode->insertBefore(clone $child,$from); #####
输出:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <html id="root"><body><p id="p1"><i>Title <b>2</b></i></p> <p id="p2">Rosangela<sup>1</sup>,<b>Eduardo<sup>4</sup></b> </p> </body></html> Removed: <b id="b1">Rosangela<sup>1</sup>,Maria<sup>2</sup></b>
我希望这有用并符合您的需求.你真的有很多代码,可能有点误导替换节点情况,这是不同的.同样在那种情况下,我修补了一些不同的错误代码,这些错误代码并不总是改变为优秀代码的最佳基础.
这顺便说一下.提醒我一个克隆也很有帮助的问题我今天刚回答: