doucuo4413 2013-09-17 12:20
浏览 71
已采纳

如何使用DOMDocumentFragment移动内部内容?

I have a horrible algorithm, to "remove a node", moving its inner content to its parent node (see below)... But I think is possible to develop a better algorithm, using DOMDocumentFragment (and not using saveXML/loadXML).

The algorithm below was inspired by 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;
 }

Example

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>

OUTPUT of moveInner($dom->getElementById('p1'))

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

OUTPUT of moveInner($dom->getElementById('b1'))

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

There are no changes in moveInner($dom->getElementById('root')), or moveInner($dom->getElementById('p1')) after first use.

PS: is like a "TRIM TAG" function.

  • 写回答

2条回答 默认 最新

  • douyin9987 2013-09-27 21:16
    关注

    As you move inside the same document this actually is by far not so much hassle. The code you've posted alone had already many places that could be optimized just on it's own, for example to turn a childNodes NodeList into an array just use iterator_to_array:

    $children = iterator_to_array($from->childNodes);
    

    Also you should use more speaking variable names, there is no problem in having longer names. It just makes the code more readable and leaves the room to view the more important stuff faster:

    /**
     * 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);
    }
    

    It just works. If you insert the same element into another place in DOMDocument, the element is moved, not duplicated.

    If you want to duplicate (so to preserve the child, not move it), you can use the child nodes as prototypes and just clone them. As this function returns the element that is removed, it the contains the copy.

    First the example w/o clone, just the function as above:

    $removed = moveInner($doc->getElementById('b1'));
    
    echo $doc->saveHTML(), "
    Removed: ", $doc->saveHTML($removed);
    

    Output:

    <!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>
    

    Then second the modified function, the change is just adding clone in the following line:

            $from->parentNode->insertBefore(clone $child, $from);
                                            #####
    

    Output:

    <!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>
    

    I hope this is helpful and matches your needs. You really had very much code there, probably a bit misleaded by the replace node scenario which is different. Also in that scenario I was patching some different error code that is not always the best base to change into good code.

    This btw. reminded me about a question where clone was also very helpful which I just answered today:

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 如何用stata画出文献中常见的安慰剂检验图
  • ¥15 c语言链表结构体数据插入
  • ¥40 使用MATLAB解答线性代数问题
  • ¥15 COCOS的问题COCOS的问题
  • ¥15 FPGA-SRIO初始化失败
  • ¥15 MapReduce实现倒排索引失败
  • ¥15 ZABBIX6.0L连接数据库报错,如何解决?(操作系统-centos)
  • ¥15 找一位技术过硬的游戏pj程序员
  • ¥15 matlab生成电测深三层曲线模型代码
  • ¥50 随机森林与房贷信用风险模型