【问题标题】:How to insert HTML to PHP DOMNode?如何将 HTML 插入 PHP DOMNode?
【发布时间】:2011-05-23 00:10:06
【问题描述】:

有什么方法可以在不编码内容的情况下将 HTML 模板插入现有 DOMNode?​​p>

我尝试过这样做:

$dom->createElement('div', '<h1>Hello world</h1>');
$dom->createTextNode('<h1>Hello world</h1>');

输出几乎相同,唯一不同的是第一个代码会将其包装在一个 div 中。 我试图从字符串中加载 HTML,但我不知道如何将它的正文内容附加到另一个 DOMDocument。

在javascript中,这个过程似乎相当简单明了。

【问题讨论】:

    标签: php html dom domdocument


    【解决方案1】:

    您可以使用

    示例:

    // just some setup
    $dom = new DOMDocument;
    $dom->loadXml('<html><body/></html>');
    $body = $dom->documentElement->firstChild;
    
    // this is the part you are looking for    
    $template = $dom->createDocumentFragment();
    $template->appendXML('<h1>This is <em>my</em> template</h1>');
    $body->appendChild($template);
    
    // output
    echo $dom->saveXml();
    

    输出:

    <?xml version="1.0"?>
    <html><body><h1>This is <em>my</em> template</h1></body></html>
    

    如果要从另一个 DOMDocument 导入,请将三行替换为

    $tpl = new DOMDocument;
    $tpl->loadXml('<h1>This is <em>my</em> template</h1>');
    $body->appendChild($dom->importNode($tpl->documentElement, TRUE));
    

    使用TRUE 作为importNode 的第二个参数将递归导入节点树。


    如果您需要导入(格式错误的)HTML,请将loadXml 更改为loadHTML。这将触发 libxml 的 HTML 解析器(ext/DOM 内部使用的):

    libxml_use_internal_errors(true);
    $tpl = new DOMDocument;
    $tpl->loadHtml('<h1>This is <em>malformed</em> template</h2>');
    $body->appendChild($dom->importNode($tpl->documentElement, TRUE));
    libxml_use_internal_errors(false);
    

    请注意,libxml 将尝试更正标记,例如它将错误的关闭 &lt;/h2&gt; 更改为 &lt;/h1&gt;

    【讨论】:

    • 我喜欢 createDocumentFragment 的第一个示例,它看起来一个命令比第二个命令短,只是出于好奇,这两个命令中哪一个更节省内存/时间?
    • @Nazariy 我不知道。我从来没有把它们放在一起,在我对应用程序所做的所有分析中,我从来没有注意到它们是一个问题。
    • 这会附加 XML,而不是 HTML?从我读过的其他答案来看,它会产生错误,因为 HTML 与“格式良好的 XML”不同。
    • @Nate appendXml 需要格式良好的 XML。如果要附加格式错误的 html,则必须调整第二种方法以使用 html 加载器。与流行的看法相反,libxml 可以很好地解析格式错误的标记。
    • @AaronGillion 我不确定你的意思。 This works fine on "nested HTML"。你能提供一个你认为它不起作用的小例子吗?那我帮你搞清楚。
    【解决方案2】:

    它与另一个 DOMDocument 一起用于解析 HTML 代码。但是你需要在主文档中import the nodes 才能在其中使用它们:

    $newDiv = $dom->createElement('div');
    $tmpDoc = new DOMDocument();
    $tmpDoc->loadHTML($str);
    foreach ($tmpDoc->getElementsByTagName('body')->item(0)->childNodes as $node) {
        $node = $dom->importNode($node, true);
        $newDiv->appendChild($node);
    }
    

    作为一个方便的功能:

    function appendHTML(DOMNode $parent, $source) {
        $tmpDoc = new DOMDocument();
        $tmpDoc->loadHTML($source);
        foreach ($tmpDoc->getElementsByTagName('body')->item(0)->childNodes as $node) {
            $node = $parent->ownerDocument->importNode($node, true);
            $parent->appendChild($node);
        }
    }
    

    那么你可以简单地这样做:

    $elem = $dom->createElement('div');
    appendHTML($elem, '<h1>Hello world</h1>');
    

    【讨论】:

    • 谢谢Gumbo,我想过,但我与getElementByTagName有点堆叠,因为在我的情况下可以有任何html内容,所以我必须提取body元素的所有子节点,如何我可以这样做吗?
    • @Nazariy:在这种情况下,获取 BODY 并为其所有子项进行导入和附加。
    • foreach 中的代码不起作用.. 因为 importNode() 返回对属于原始 DOMDocument 的 NEW 节点的引用,因此,为了能够附加它,您应该保留它参考..您当前正在执行的操作是导入节点并尝试将 tmpDoc 的节点附加到属于原始文档的 $parent 。正确的程序是:$importedNode = $parent-&gt;ownerDocument-&gt;importNode($node, TRUE); $parent-&gt;appendChild($importedNode);
    • DOMDocument::loadHTML() 的 cmets 中提到了我在使用此解决方案时遇到的问题,其中如果 $str(或 $source,在您的第二个示例中)包含任何文本 not 包含在 HTML 元素中,它将被包裹在 &lt;p&gt; 标记中。有什么方法可以阻止loadHTML() 在实际文本节点上放置&lt;p&gt; 标签?
    • 另一种解决方案在我看来更简单
    【解决方案3】:

    因为我不想与 XML 作斗争,因为它会更快地引发错误,而且我不喜欢使用 @ 前缀来防止错误输出。我认为 loadHTML 做得更好,而且很简单:

    $doc = new DOMDocument();
    $div = $doc->createElement('div');
    
    // use a helper to load the HTML into a string
    $helper = new DOMDocument();
    $helper->loadHTML('<a href="#">This is my HTML Link.</a>');
    
    // now the magic!
    // import the document node of the $helper object deeply (true)
    // into the $div and append as child.
    $div->appendChild($doc->importNode($helper->documentElement, true));
    
    // add the div to the $doc
    $doc->appendChild($div);
    
    // final output
    echo $doc->saveHTML();
    

    【讨论】:

    • LIBXML_HTML_NODEFDTD | LIBXML_HTML_NOIMPLIED 配合得很好
    【解决方案4】:

    这是使用DOMDocumentFragment的简单示例:

    $doc = new DOMDocument();
    $doc->loadXML("<root/>");
    $f = $doc->createDocumentFragment();
    $f->appendXML("<foo>text</foo><bar>text2</bar>");
    $doc->documentElement->appendChild($f);
    echo $doc->saveXML();
    

    这里是替换 DOMNode 的辅助函数:

    /** 
     * Helper function for replacing $node (DOMNode) 
     * with an XML code (string) 
     * 
     * @var DOMNode $node 
     * @var string $xml 
     */ 
    public function replaceNodeXML(&$node, $xml) { 
      $f = $this->dom->createDocumentFragment(); 
      $f->appendXML($xml); 
      $node->parentNode->replaceChild($f,$node); 
    }
    

    来源:一些旧的“PHP5 Dom Based Template”文章。

    这是Pian0_M4n 发布的另一个建议,使用 value 属性作为解决方法:

    $dom = new DomDocument;
    
    // main object
    $object = $dom->createElement('div');
    
    // html attribute
    $attr = $dom->createAttribute('value');
    // ugly html string
    $attr->value = "<div>&nbsp; this is a really html string &copy;</div><i></i> with all the &copy; that XML hates!";
    $object->appendChild($attr);
    
    // jquery fix (or javascript as well)
    $('div').html($(this).attr('value')); // and it works! 
    $('div').removeAttr('value'); // to clean-up
    

    没有理想,但至少可以。

    【讨论】:

      【解决方案5】:

      Gumbo 的代码完美运行!只是添加 TRUE 参数的一点改进,以便它与嵌套的 html sn-ps 一起使用。

      $node = $parent->ownerDocument->importNode($node);
      $node = $parent->ownerDocument->importNode($node, **TRUE**);
      

      【讨论】:

      • @MarcelBurkhard,不,应该是评论。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-16
      • 2021-10-05
      • 1970-01-01
      相关资源
      最近更新 更多