将任意HTML插入DocumentFragment

时间:2012-02-14 21:00:44

标签: javascript jquery documentfragment

我知道最近已经讨论了adding innerHTML to document fragments,并希望将其纳入DOM标准。但是,在此期间您应该使用的解决方法是什么?

即,

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

我希望div内的spanfrag都有一个简单的单行内容。

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

11 个答案:

答案 0 :(得分:82)

这是modern browsers中没有循环的方式:

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/

答案 1 :(得分:24)

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

  • 由于它没有附加到文档中,因此没有任何内容呈现,因此没有性能损失。
  • 你看到一个循环,但它只是循环通过第一个孩子。大多数文档只有一些半根元素,所以这也不是什么大问题。

如果要创建整个文档,请改用DOMParser。看看this answer

代码:

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);

答案 2 :(得分:10)

使用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中的自定义元素将立即使用范围进行升级,但仅在使用模板克隆到真实文档中时才会升级。模板方法更加“惰性”,这可能是理想的。

答案 3 :(得分:5)

@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块而受到影响,但是,它与许多旧版浏览器兼容并且简洁。

答案 4 :(得分:2)

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对象。准备好追加它之后,只需将其附加到页面上的现有元素

即可

答案 5 :(得分:2)

就像@dandavis所说,使用template-tag有一种标准的方法 但是如果你想支持IE11并且你需要解析像&#39;&lt; td&gt; test&#39;这样的表格元素,你可以使用这个函数:

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;
}

答案 6 :(得分:1)

这是一个x浏览器解决方案,在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;
        }
    }

答案 7 :(得分: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对象中,并免费获得所有必需的方法。

您可以在https://developer.mozilla.org/en-US/docs/Web/API/Range

中找到所有Range方法和属性的列表。

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

答案 8 :(得分:0)

要使用尽可能少的行来执行此操作,您可以将您的内容包装在另一个div中,这样您就不必多次循环或调用appendchild。使用jQuery(如您所述),您可以非常快速地创建一个未连接的dom节点并将其放在片段中。

var html = '<div id="main"><div>x</div><span>y</span></div>';
var frag = document.createDocumentFragment();
frag.appendChild($​(html)[0]);

答案 9 :(得分: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);

答案 10 :(得分:0)

没有人提供所要求的“简单一线”。

给出变量...

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);