Inserting arbitrary HTML into a DocumentFragment

前端 未结 11 1928
花落未央
花落未央 2020-11-29 18:53

I know that adding innerHTML to document fragments has been recently discussed, and will hopefully see inclusion in the DOM Standard. But, what is the workaround you\'re sup

相关标签:
11条回答
  • 2020-11-29 19:01

    Like @dandavis said, there is a standard way by using the template-tag.
    But if you like to support IE11 and you need to parse table elements like '<td>test', you can use this function:

    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;
    }
    
    0 讨论(0)
  • 2020-11-29 19:03

    Currently, the only way to fill a document fragment using only a string is to create a temporary object, and loop through the children to append them to the fragment.

    • Since it's not appended to the document, nothing is rendered, so there's no performance hit.
    • You see a loop, but it's only looping through the first childs. Most documents have only a few semi-root elements, so that's not a big deal either.

    If you want to create a whole document, use the DOMParser instead. Have a look at this answer.

    Code:

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

    A one-liner (two lines for readability) (input: String html, output: DocumentFragment frag):

    var frag =document.createDocumentFragment(), t=document.createElement('body'), c;
    t.innerHTML = html; while(c=t.firstElementChild) frag.appendChild(c);
    
    0 讨论(0)
  • 2020-11-29 19:05

    Here is a way in modern browsers without looping:

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

    or, as a re-usable

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

    UPDATE: I found a simpler way to use Pete's main idea, which adds IE11 to the mix:

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

    The coverage is better than the <template> method and tested ok in IE11, Ch, FF.

    Live test/demo available http://pagedemos.com/str2fragment/

    0 讨论(0)
  • 2020-11-29 19:06

    No one ever provided the requested "easy one-liner".

    Given the variables…

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

    … the following line will do the trick (in Firefox 67.0.4):

    frag.append(...new DOMParser().parseFromString(html, "text/html").body.childNodes);
    
    0 讨论(0)
  • 2020-11-29 19:06

    I would go with something like this..

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

    This way you keep the content inside a Range object and you get all the needed methods for free.

    You can find the list of all Range methods and properties here https://developer.mozilla.org/en-US/docs/Web/API/Range

    Note: Remember to use detatch() method once you are done with it to avoid leaks and improve performance.

    0 讨论(0)
  • 2020-11-29 19:07

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

    Note that the primary differences between this approach and the <template> approach are:

    • Range.createContextualFragment is a bit more widely supported (IE11 just got it, Safari, Chrome and FF have had it for a while).

    • Custom elements within the HTML will be upgraded immediately with the range, but only when cloned into the real doc with template. The template approach is a bit more 'inert', which may be desirable.

    0 讨论(0)
提交回复
热议问题