64

我知道最近讨论了添加innerHTML到文档片段,并希望看到包含在 DOM 标准中。但是,在此期间您应该使用什么解决方法?

也就是说,取

var html = '<div>x</div><span>y</span>';
var frag = document.createDocumentFragment();

我想要div的内部和span内部frag,带有一个简单的单线。

没有循环的奖励积分。允许使用 jQuery,但我已经尝试过$(html).appendTo(frag)frag之后仍然是空的。

4

11 回答 11

101

这是现代浏览器中一种不循环的方法:

var temp = document.createElement('template');
temp.innerHTML = '<div>x</div><span>y</span>';

var frag = temp.content;

或者,作为可重复使用的

function fragmentFromString(strHTML) {
    var temp = document.createElement('template');
    temp.innerHTML = strHTML;
    return temp.content;
}

更新:我找到了一种更简单的方法来使用 Pete 的主要思想,它将 IE11 添加到组合中:

function fragmentFromString(strHTML) {
    return document.createRange().createContextualFragment(strHTML);
}

覆盖率优于该<template>方法,在IE11、Ch、FF中测试正常。

现场测试/演示可用http://pagedemos.com/str2fragment/

于 2014-08-09T00:36:48.153 回答
23

目前,仅使用字符串填充文档片段的唯一方法是创建一个临时对象,然后循环遍历子对象以将它们附加到片段中。

  • 由于它没有附加到文档中,因此不会渲染任何内容,因此不会影响性能。
  • 您会看到一个循环,但它只是循环通过第一个孩子。大多数文档只有几个半根元素,所以这也不是什么大问题。

如果要创建整个文档,请改用 DOMParser。看看这个答案

代码:

var frag = document.createDocumentFragment(),
    tmp = document.createElement('body'), child;
tmp.innerHTML = '<div>x</div><span>y</span>';
while (child = tmp.firstElementChild) {
    frag.appendChild(child);
}

单行(两行便于阅读)(输入:String html,输出:) DocumentFragment frag

var frag =document.createDocumentFragment(), t=document.createElement('body'), c;
t.innerHTML = html; while(c=t.firstElementChild) frag.appendChild(c);
于 2012-02-14T22:17:38.743 回答
12

使用Range.createContextualFragment

var html = '<div>x</div><span>y</span>';
var range = document.createRange();
// or whatever context the fragment is to be evaluated in.
var parseContext = document.body; 
range.selectNodeContents(parseContext);
var fragment = range.createContextualFragment(html);

请注意,此方法与该方法之间的主要区别<template>是:

  • Range.createContextualFragment 得到了更广泛的支持(IE11 刚刚得到它,Safari、Chrome 和 FF 已经有一段时间了)。

  • HTML 中的自定义元素将立即随范围升级,但仅在使用模板克隆到真实文档时。模板方法有点“惰性”,这可能是可取的。

于 2014-08-10T05:56:32.580 回答
7

从来没有人提供过要求的“简单的单线”。

给定变量……</p>

var html = '<div>x</div><span>y</span>';
var frag = document.createDocumentFragment();

......下面的行将解决问题(在 Firefox 67.0.4 中):

frag.append(...new DOMParser().parseFromString(html, "text/html").body.childNodes);
于 2019-06-27T19:38:43.333 回答
4

@PAEz 指出@RobW 的方法不包括元素之间的文本。那是因为children只抓取Elements,而不是Nodes。更稳健的方法可能如下:

var fragment = document.createDocumentFragment(),
    intermediateContainer = document.createElement('div');

intermediateContainer.innerHTML = "Wubba<div>Lubba</div>Dub<span>Dub</span>";

while (intermediateContainer.childNodes.length > 0) {
    fragment.appendChild(intermediateContainer.childNodes[0]);
}

性能可能会在较大的 HTML 块上受到影响,但是,它与许多旧浏览器兼容,并且简洁。

于 2016-05-16T17:56:23.643 回答
3

createDocumentFragment 创建一个空的 DOM “容器”。innerHtml 和其他方法仅适用于 DOM 节点(而不是容器),因此您必须先创建节点,然后将它们添加到片段中。您可以使用一种痛苦的 appendChild 方法来做到这一点,或者您可以创建一个节点并修改它的 innerHtml 并将其添加到您的片段中。

var frag = document.createDocumentFragment();
    var html = '<div>x</div><span>y</span>';
var holder = document.createElement("div")
holder.innerHTML = html
frag.appendChild(holder)

使用 jquery,您只需将 html 保存并构建为字符串。如果要将其转换为 jquery 对象以对其执行类似 jquery 的操作,只需执行 $(html) 即可在内存中创建一个 jquery 对象。准备好附加它后,只需将其附加到页面上的现有元素

于 2012-02-14T21:59:29.680 回答
2

就像@dandavis 所说,有一种使用模板标签的标准方法。
但是如果你喜欢支持 IE11 并且需要解析像'<td>test'这样的表格元素,你可以使用这个函数:

function createFragment(html){
    var tmpl = document.createElement('template');
    tmpl.innerHTML = html;
    if (tmpl.content == void 0){ // ie11
        var fragment = document.createDocumentFragment();
        var isTableEl = /^[^\S]*?<(t(?:head|body|foot|r|d|h))/i.test(html);
        tmpl.innerHTML = isTableEl ? '<table>'+html : html;
        var els        = isTableEl ? tmpl.querySelector(RegExp.$1).parentNode.childNodes : tmpl.childNodes;
        while(els[0]) fragment.appendChild(els[0]);
        return fragment;
    }
    return tmpl.content;
}
于 2017-04-08T11:16:33.737 回答
1

这是一个 x-browser 解决方案,在 IE10、IE11、Edge、Chrome 和 FF 上进行了测试。

    function HTML2DocumentFragment(markup: string) {
        if (markup.toLowerCase().trim().indexOf('<!doctype') === 0) {
            let doc = document.implementation.createHTMLDocument("");
            doc.documentElement.innerHTML = markup;
            return doc;
        } else if ('content' in document.createElement('template')) {
            // Template tag exists!
            let el = document.createElement('template');
            el.innerHTML = markup;
            return el.content;
        } else {
            // Template tag doesn't exist!
            var docfrag = document.createDocumentFragment();
            let el = document.createElement('body');
            el.innerHTML = markup;
            for (let i = 0; 0 < el.childNodes.length;) {
                docfrag.appendChild(el.childNodes[i]);
            }
            return docfrag;
        }
    }
于 2017-08-17T14:46:57.873 回答
1

我会选择这样的东西..

function fragmentFromString(html) {
  const range = new Range();
  const template = range.createContextualFragment(html);
  range.selectNode(template.firstElementChild);
  return range;
}

// Append to body
// document.body.append(fragmentFromString(`<div>a</div>`).cloneContents())

通过这种方式,您可以将内容保存在 Range 对象中,并免费获得所有需要的方法。

您可以在这里找到所有 Range 方法和属性的列表https://developer.mozilla.org/en-US/docs/Web/API/Range

注意:记住在detatch()完成后使用方法以避免泄漏并提高性能。

于 2020-10-19T10:11:48.370 回答
0
var html = '<div>x</div><span>y</span>';
var frag = document.createDocumentFragment();
var e = document.createElement('i');
frag.appendChild(e);
e.insertAdjacentHTML('afterend', html);
frag.removeChild(e);
于 2017-02-17T16:05:30.617 回答
-1

要使用尽可能少的行来做到这一点,您可以将上面的内容包装在另一个 div 中,这样您就不必多次循环或调用 appendchild。使用 jQuery(正如您提到的那样),您可以非常快速地创建一个未附加的 dom 节点并将其放置在片段中。

var html = '<div id="main"><div>x</div><span>y</span></div>';
var frag = document.createDocumentFragment();
frag.appendChild($​(html)[0]);
于 2012-02-14T21:59:06.163 回答