使用 document.createDocumentFragment() 和 innerHTML 来操作 DOM

Posted

技术标签:

【中文标题】使用 document.createDocumentFragment() 和 innerHTML 来操作 DOM【英文标题】:using document.createDocumentFragment() and innerHTML to manipulate a DOM 【发布时间】:2012-01-02 09:13:22 【问题描述】:

我正在创建一个文档片段,如下所示:

var aWholehtmlDocument = '<!doctype html> <html><head></head><body><h1>hello world</h1></body></html>';
var frag = document.createDocumentFragment();
frag.innerHTML = aWholeHTMLDocument;

变量aWholeHTMLDocument包含一个长字符串,它是一个页面的整个html文档,我想将它插入到我的fragment中,以便动态生成和操作DOM。 p>

我的问题是,一旦我将该字符串添加到frag.innerHTML,它不应该加载该字符串并将其转换为 DOM 对象吗?

设置innerHTML后,我不应该通过属性访问DOM吗?

我尝试了 frag.childNodes 但它似乎不包含任何内容,我只想访问新创建的 DOM。

【问题讨论】:

我不确定任何 DOM 元素的“innerHTML”(文档片段实际上只是一个不能成为 DOM 一部分的 DOM 元素)是否是完整的HTML 文档。如果可能的话,您的问题的答案是“是”。 你只做一次 .innerHTML 而不是 .appendChild(frag) 。它比创建文档片段更快,因为字符串处理更快。 证明:jsperf.com/fragments-vs-html-strings 【参考方案1】:

你can't set the innerHTML of a document fragment 就像你对普通节点所做的那样,这就是问题所在。添加标准 div 并设置其 innerHTML 是常见的解决方案。

【讨论】:

那么您的意思是我应该执行以下操作? var aWholeHTMLDocument = '

hello world

'; var div = document.createElement('div'); div.innerHTML = 一个WholeHTMLDocument;这将删除除

之外的所有内容...

@Loic - 他从来没有说过这样的话......他只是解释了你的错误。绝对不推荐您在评论中建议的解决方案。【参考方案2】:

使用文档片段,您可以附加使用document.createElement('yourElement') 创建的元素。 aWholeHTMLDocument 只是文字。此外,除非您使用框架,否则我不确定您为什么需要创建整个 HTML 文档,只需使用 &lt;body&gt; 标记内的内容即可。

【讨论】:

这是一个 Firefox 插件。我将 aWholeHTMLDocument 放入 iFrame,但我需要它完整,而不仅仅是 标签, 标签还包含感兴趣的东西,我想保持整个 DOM 完整。 @Loic 那么你想要的是获得对iframe 的引用并将其innerHTML 设置为aWholeHTMLDocument【参考方案3】:

对于 Firefox 插件,使用 document.implementation.createHTMLDocument 方法可能更有意义,然后从提供给您的 DOM 中获取。

【讨论】:

【参考方案4】:

我知道这个问题很老了,但我在玩文档片段时遇到了同样的问题,因为我没有意识到我必须向它附加一个 div 并使用 div 的innerHTML 来加载 HTML 字符串并从中获取 DOM 元素。我有其他关于如何做这类事情的答案,更适合整个文档。

在 firefox (23.0.1) 中,设置文档片段的 innerHTML 属性似乎不会自动生成元素。只有在将片段附加到文档之后才会创建元素。

要创建整个文档,请使用document.implementation 方法(如果它们受支持)。我已经在 Firefox 上成功了,但我还没有在其他浏览器上真正测试过。您可以查看 AtropaToolbox 中的 HTMLParser.js 以获取使用 document.implementation 方法的示例。我已经使用这段脚本来处理XMLHttpRequest 页面并对其进行操作或从中提取数据。虽然页面中的脚本没有执行,但这是我想要的,虽然它可能不是你想要的。我使用这种相当冗长的方法而不是尝试直接使用 XMLHttpRequest 对象提供的解析的原因是当时我遇到了很多解析错误的麻烦,我想指定文档应该是被解析为 HTML 4 过渡,因为它似乎采取了各种 slop 并生成 DOM。

还有一个DOMParser 可用,您可能更容易使用它。 Eli Gray 在 MDN 的页面上有一个针对没有 DOMParser 但支持 document.implementation.createHTMLDocument 的浏览器的实现。 DOMParser 的规范指定不执行页面中的脚本,并且呈现 noscript 标记的内容。

如果您确实需要在页面中启用脚本,您可以创建一个高度为 0、宽度为 0、无边框等的 iFrame。它仍然会在页面中,但您可以很好地隐藏它。

您还可以选择使用window.open()document.write、DOM 方法或任何您喜欢的方法。现在有些浏览器甚至允许你做数据 URI。

var x = window.open( 'data:text/html;base64,' + btoa('<h1>hi</h1>') );
// wait for the document to load. It only takes a few milliseconds
// but we'll wait for 5 seconds so you can watch the child window
// change.
setTimeout(function () 
    console.log(x.document.documentElement.outerHTML);
    x.console.log('this is the console in the child window');
    x.document.body.innerHTML = 'oh wow';
, 5000);

因此,您确实有一些选项可以在屏幕外/隐藏创建整个文档并对其进行操作,所有这些都支持从字符串加载文档。

还有phantomjs,这是一个很棒的项目,它产生了一个基于 webkit 的无头可编写脚本的网络浏览器。您将可以访问本地文件系统,并且几乎可以做任何您想做的事情。我真的不知道您要通过整页脚本和操作来完成什么。

【讨论】:

【参考方案5】:

使用 querySelector() 来获取文档片段的子项(您可能想要正文,或者正文的某个子项)。然后获取innerHTML。

document.body.innerHTML = aWholeHTMLDocument.querySelector("body").innerHTML

aWholeHTMLDocument.querySelector("body").childNodes;

见https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment.querySelector

【讨论】:

【参考方案6】:

使用appendChild

见https://developer.mozilla.org/en-US/docs/Web/API/Document/createDocumentFragment

var fragment = document.createDocumentFragment();
... fragment.appendChild(some element);
document.querySelector('blah').appendChild(fragment);

【讨论】:

这不能解决“进程字符串”问题。【参考方案7】:

虽然DocumentFragment 不支持innerHTML,但&lt;template&gt; 支持

&lt;template&gt; 元素的content 属性是DocumentFragment,因此其行为方式相同。例如,您可以这样做:

var tpl = document.createElement('template');
tpl.innerHTML = '<tr><td>Hello</td><td>world</td></tr>';
document.querySelector('table').appendChild(tpl.content);

上面的例子很重要,因为你不能用innerHTML 和例如。 &lt;div&gt;,因为 &lt;div&gt; 不允许 &lt;tr&gt; 元素作为子元素。


注意:DocumentFragment 仍会剥离 &lt;head&gt;&lt;body&gt; 标记,因此它也不会执行您想要的操作。你真的需要创建一个全新的Document

【讨论】:

真的,这应该是公认的答案,因为它也提供了一个非常好的解决方案。 @Chowey:table 中的 tr 元素存在于 theadtbodytfoot 中。【参考方案8】:

DocumentFragment 继承自 Node,但不继承自包含 .innerHTML 属性的 Element

在你的情况下,我会使用&lt;template&gt; 标签。 In 继承自 Element,它有一个漂亮的 HTMLTemplateElement.content 属性,可以为您提供 DocumentFragment

这是一个您可以使用的简单辅助方法:

export default function StringToFragment(string) 
    var renderer = document.createElement('template');
    renderer.innerHTML = string;
    return renderer.content;

【讨论】:

【参考方案9】:

这是一个将 HTML 字符串转换为 DOM 对象的解决方案:

let markup = '<!doctype html><html><head></head><body><h1>hello world</h1></body></html>';
let range = document.createRange();
let fragment = range.createContextualFragment(markup); //Creates a DOM object

字符串不必是完整的 HTML 文档。

【讨论】:

以上是关于使用 document.createDocumentFragment() 和 innerHTML 来操作 DOM的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)