如何将 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 将尝试更正标记,例如它将错误的关闭 </h2>
更改为 </h1>
。
【讨论】:
我喜欢 createDocumentFragment 的第一个示例,它看起来一个命令比第二个命令短,只是出于好奇,这两个命令中哪一个更节省内存/时间? @Nazariy 我不知道。我从来没有把它们放在一起,在我对应用程序所做的所有分析中,我从来没有注意到它们是一个问题。 这会附加 XML,而不是 HTML?从我读过的其他答案来看,它会产生错误,因为 HTML 与“格式良好的 XML”不同。 @NateappendXml
需要格式良好的 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->ownerDocument->importNode($node, TRUE); $parent->appendChild($importedNode);
DOMDocument::loadHTML() 的 cmets 中提到了我在使用此解决方案时遇到的问题,其中如果 $str
(或 $source
,在您的第二个示例中)包含任何文本 not 包含在 HTML 元素中,它将被包裹在 <p>
标记中。有什么方法可以阻止loadHTML()
在实际文本节点上放置<p>
标签?
另一种解决方案在我看来更简单【参考方案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> this is a really html string ©</div><i></i> with all the © 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