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

49

我正在按照以下方式创建文档片段:

var aWholeHTMLDocument = '<!doctype html> <html><head></head><body><h1>hello world</h1></body></html>';
var frag = document.createDocumentFragment();
frag.innerHTML = aWholeHTMLDocument;
变量aWholeHTMLDocument包含一个长字符串,即整个页面的HTML文档,我想将其插入到我的片段中,以便动态生成和操作DOM。
我的问题是,一旦我将该字符串添加到frag.innerHTML中,它不应该加载此字符串并将其转换为DOM对象吗?
在设置innerHTML之后,我是否可以通过属性访问DOM?
我尝试过frag.childNodes,但似乎没有包含任何内容,我只想访问那个新创建的DOM。

我不确定任何DOM元素的“innerHTML”(文档片段实际上只是一个不能成为DOM一部分的DOM元素)是否可以是完整的HTML文档。如果可能的话,你问题的答案就是“是”。 - Pointy
不要使用.appendChild(frag),而是一次性使用.innerHTML。这样比创建文档片段更快,因为字符串处理更快。 - user985399
证明:https://jsperf.com/fragments-vs-html-strings - user985399
9个回答

50

DocumentFragment 不支持 innerHTML,但是 <template> 元素支持。

<template> 元素的 content 属性是一个 DocumentFragment,因此其行为与 DocumentFragment 相同。例如,您可以执行以下操作:

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

上面的示例很重要,因为你不能使用 innerHTML 和例如 <div> 来做到这一点,因为 <div> 不允许 <tr> 元素作为子元素。


注意:DocumentFragment 仍然会剥离 <head><body> 标签,因此它也无法达到你想要的效果。你真的需要创建一个全新的 Document


1
实际上,这应该是被接受的答案,因为它还提供了一个非常好的解决方案。@Chowey:table内的tr元素位于theadtbodytfoot中。 - connexo
这是我找到的唯一一个不会在插入的内容周围添加包装器div的答案。当以编程方式将html内容插入到Web组件的插槽中时,非常有帮助。 - borie88

37

你无法像操作普通节点一样设置文档片段的innerHTML,这就是问题所在。添加一个标准div并设置其innerHTML是常见的解决方案。


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

hello world

';
var div = document.createElement('div'); div.innerHTML = aWholeHTMLDocument;这将除了<h1>标签以外的所有内容都剥离掉...
- Loic Duros
4
他从未说过类似这样的话......他只是解释了你的错误。你在评论中提出的解决方案绝对不被推荐。 - ˈvɔlə

15

DocumentFragment 继承自 Node,而不是含有 .innerHTML 属性的 Element

在您的情况下,我会使用 <template> 标签。它继承自 Element,并具有一个很棒的 HTMLTemplateElement.content 属性,可以为您提供一个 DocumentFragment

以下是一个简单的帮助方法可以使用:

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

6
我知道这个问题很老,但是当我尝试使用文档片段时遇到了同样的问题,因为我没有意识到我必须将一个 div 追加到它上面,并使用 div 的 innerHTML 将 HTML 字符串加载进来并获取 DOM 元素。我有其他关于如何做这种事情的答案,更适用于整个文档。
在 Firefox(23.0.1)中,设置文档片段的 innerHTML 属性似乎不会自动生成元素。只有将片段附加到文档后,元素才会被创建。
要创建一个完整的文档,请使用document.implementation方法(如果支持)。我在Firefox上成功实现了这一点,但在其他浏览器上并没有进行过真正的测试。您可以查看AtropaToolbox中的HTMLParser.js以了解使用document.implementation方法的示例。我使用这段脚本来XMLHttpRequest页面并从中提取数据或操作它们。但是,页面中的脚本不会被执行,这正是我想要的,尽管可能不是您想要的。我之所以选择这种冗长的方法而不是直接使用XMLHttpRequest对象可用的解析,是因为当时遇到了很多解析错误,并且我想指定将文档解析为HTML 4 Transitional,因为它似乎可以处理各种松散的代码并生成DOM。

同时还有一个DOMParser可用,这可能更容易使用。Eli Grey在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的可编程无头浏览器的令人惊叹的项目。您将可以访问本地文件系统并且能够做几乎任何您想要做的事情。我不知道您正在尝试使用完整页面脚本和操作来实现什么目标。

2

1

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


这是一个Firefox插件的内容。我正在将一个完整的HTML文档放入一个iFrame中,但我需要保留它的整个结构,而不仅仅是<body>标签,<head>标签也包含感兴趣的内容,我想保持整个DOM文档的完整性。 - Loic Duros
@Loic,那么您想要的是获取对“iframe”的引用,并将其innerHTML设置为“aWholeHTMLDocument”。 - qw3n

1

3
这并未解决“进程字符串”问题。 - Jorge Fuentes González

1
这里有一种将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文档。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接