Creating a new DOM element from an HTML string using built-in DOM methods or Prototype

Creating a new DOM element from an HTML string using built-in DOM methods or Prototype

I have an HTML string representing an element: ‘

  • text
  • ‘. I’d like to append it to an element in the DOM (a ul in my case). How can I do this with Prototype or with DOM methods?
    (I know i could do this easily in jQuery, but unfortunately we’re not using jQuery.)

    Solutions/Answers:

    Solution 1:

    Note: most current browsers support HTML <template> elements, which provide a more reliable way of turning creating elements from strings. See Mark Amery’s answer below for details.

    For older browsers, and node/jsdom: (which doesn’t yet support <template> elements at the time of writing), use the following method. It’s the same thing the libraries use to do to get DOM elements from an HTML string (with some extra work for IE to work around bugs with its implementation of innerHTML):

    function createElementFromHTML(htmlString) {
      var div = document.createElement('div');
      div.innerHTML = htmlString.trim();
    
      // Change this to div.childNodes to support multiple top-level nodes
      return div.firstChild; 
    }
    

    Note that unlike HTML templates this won’t work for some elements that cannot legally be children of a <div>, such as <td>s.

    If you’re already using a library, I would recommend you stick to the library-approved method of creating elements from HTML strings:

    Solution 2:

    HTML 5 introduced the <template> element which can be used for this purpose (as now described in the WhatWG spec and MDN docs).

    A <template> is an HTML element which is allowed any other element type as a child. The template has a .content property that you can access with JavaScript which points to a DocumentFragment with the template’s contents. This means that you can convert a HTML string to DOM elements by setting the innerHTML of a <template> element, then reaching into the template‘s .content property.

    Examples:

    /**
     * @param {String} HTML representing a single element
     * @return {Element}
     */
    function htmlToElement(html) {
        var template = document.createElement('template');
        html = html.trim(); // Never return a text node of whitespace as the result
        template.innerHTML = html;
        return template.content.firstChild;
    }
    
    var td = htmlToElement('<td>foo</td>'),
        div = htmlToElement('<div><span>nested</span> <span>stuff</span></div>');
    
    /**
     * @param {String} HTML representing any number of sibling elements
     * @return {NodeList} 
     */
    function htmlToElements(html) {
        var template = document.createElement('template');
        template.innerHTML = html;
        return template.content.childNodes;
    }
    
    var rows = htmlToElements('<tr><td>foo</td></tr><tr><td>bar</td></tr>');
    

    Note that similar approaches that use a different container element such as a div don’t quite work. HTML has restrictions on what element types are allowed to exist inside which other element types; for instance, you can’t put a td as a direct child of a div. This causes these elements to vanish if you try to set the innerHTML of a div to contain them. Since <template>s have no such restrictions on their content, this shortcoming doesn’t apply when using a template.

    However, template is not supported in some old browsers. As of January 2018, Can I use… estimates 90% of users globally are using a browser that supports templates. In particular, no version of Internet Explorer supports them; Microsoft did not implement template support until the release of Edge.

    If you’re lucky enough to be writing code that’s only targeted at users on modern browsers, go ahead and use them right now. Otherwise, you may have to wait a while for users to catch up.

    Solution 3:

    Use insertAdjacentHTML(). It works with all current browsers, even with IE11.

    var mylist = document.getElementById('mylist');
    mylist.insertAdjacentHTML('beforeend', '<li>third</li>');
    <ul id="mylist">
     <li>first</li>
     <li>second</li>
    </ul>

    Solution 4:

    Newer DOM implementations have range.createContextualFragment, which does what you want in a framework-independent way.

    It’s widely supported. To be sure though, check its compatibility down in the same MDN link, as it will be changing. As of May 2017 this is it:

    Feature         Chrome   Edge   Firefox(Gecko)  Internet Explorer   Opera   Safari
    Basic support   (Yes)    (Yes)  (Yes)           11                  15.0    9.1.2
    

    Solution 5:

    No need for any tweak, you got a native API:

    const toNodes = html =>
        new DOMParser().parseFromString(html, 'text/html').body.childNodes
    

    Solution 6:

    Heres a simple way to do it:

    String.prototype.toDOM=function(){
      var d=document
         ,i
         ,a=d.createElement("div")
         ,b=d.createDocumentFragment();
      a.innerHTML=this;
      while(i=a.firstChild)b.appendChild(i);
      return b;
    };
    
    var foo="<img src='//placekitten.com/100/100'>foo<i>bar</i>".toDOM();
    document.body.appendChild(foo);