如何将 HTML 插入 PHP DOMNode?

Posted

技术标签:

【中文标题】如何将 HTML 插入 PHP DOMNode?【英文标题】:How to insert HTML to 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中,这个过程似乎相当简单明了。

【问题讨论】:

【参考方案1】:

您可以使用

DOMDocumentFragment::appendXML — Append raw XML data

示例:

// 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,不,应该是评论。

以上是关于如何将 HTML 插入 PHP DOMNode?的主要内容,如果未能解决你的问题,请参考以下文章

在 PHP 中从 DOMNode 创建 DOMDocument

什么会导致 DOMNode::nodeValue 为空?

在博客中插入图片

如何将 PHP 输出插入 html div 替换临时图形

如何通过 PHP 将未知数量的 HTML 表单字段插入 MySQL?

如何将 php 代码插入 HTML 中的短代码?