在DocumentFragment中插入任意HTML

Inserting arbitrary HTML into a DocumentFragment

本文关键字:任意 HTML 插入 DocumentFragment      更新时间:2023-09-26

我知道最近已经讨论过将innerHTML添加到文档片段中,并有望将其包含在DOM标准中。但是,在此期间,您应该使用什么变通方法?

也就是说,采用

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

我想要divspan都在frag里面,有一个简单的一行。

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

在现代浏览器中有一种不循环的方法:

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/

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

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

如果您想创建一个完整的文档,请改用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);

使用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中的自定义元素将立即随范围升级,但仅当使用模板克隆到真实文档中时。模板方法有点"惰性",这可能是可取的。

从来没有人提供过所要求的"简单的一行代码"。

给定变量…

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

@PAEz指出@RobW的方法不包括元素之间的文本。这是因为children只抓取元素,而不抓取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块可能会影响性能,但是,它与许多较旧的浏览器兼容,而且简洁。

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对象。一旦您准备好附加它,只需将它附加到页面上的现有元素即可

就像@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;
}

我会选择这样的东西。。

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()方法后,请记住使用该方法,以避免泄漏并提高性能。

这是一个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;
        }
    }
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);

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

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