【问题标题】:How to move inner content with DOMDocumentFragment?如何使用 DOMDocumentFragment 移动内部内容?
【发布时间】:2013-09-21 21:10:42
【问题描述】:

我有一个可怕的算法,“删除一个节点”,将它的内部内容移动到它的父节点(见下文)......但是我认为可以开发一个更好的算法,使用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;
 }

示例

输入

<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-&gt;getElementById('p1'))的输出

... <p id="p1">Title</p> ...

moveInner($dom-&gt;getElementById('b1'))的输出

... <p id="p2">Rosangela<sup>1</sup>, Maria<sup>2</sup>, 
        <b>Eduardo<sup>4</sup></b>
    </p> ...

首次使用后moveInner($dom-&gt;getElementById('root'))moveInner($dom-&gt;getElementById('p1')) 没有变化。

PS:就像一个“TRIM TAG”功能。

【问题讨论】:

    标签: php domdocument innerhtml


    【解决方案1】:

    当您在同一个文档中移动时,这实际上并没有那么麻烦。您单独发布的代码已经有很多地方可以单独优化,例如将childNodes NodeList 转换为数组只需使用iterator_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);
    }
    

    它只是工作。如果将 same 元素插入 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>, Maria<sup>2</sup>,
               <b>Eduardo<sup>4</sup></b>
    </p>
    </body></html>
    
    Removed: <b id="b1"></b>
    

    然后是修改后的函数,变化只是在下面一行添加clone

            $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>, Maria<sup>2</sup>,
               <b>Eduardo<sup>4</sup></b>
    </p>
    </body></html>
    
    Removed: <b id="b1">Rosangela<sup>1</sup>, Maria<sup>2</sup></b>
    

    我希望这对您有所帮助并符合您的需求。你真的有很多代码,可能有点被不同的替换节点场景误导了。同样在那种情况下,我正在修补一些不同的错误代码,这些错误代码并不总是最好的基础来更改为好的代码。

    顺便说一句。让我想起了一个问题,克隆也很有帮助,我今天刚刚回答了:

    【讨论】:

      【解决方案2】:

      您可以尝试使用 phpQuery 来完成此任务。它的语法类似于 jquery。 像这样的东西pq("#b1")-&gt;html(pq("#b1")-&gt;text()); 链接到phpQuery

      【讨论】:

      • 感谢关于 phpQuery 的线索,但 这不是答案:问题是关于编程 DomDocument... 你能提取一个真正能做什么的 phpQuery 库片段吗?问个问题?
      猜你喜欢
      • 2020-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-20
      • 1970-01-01
      • 2018-04-10
      • 1970-01-01
      相关资源
      最近更新 更多