22

I want to create HTMLElement from string by javasacript, like this

element = createHTMLElement('<table class="list"><tr><td><a href="xxx">title</a></td></tr></table>')
element.addEventListener(...)
parent.appendChild(element)

and I do not want to use jQuery

guilin 桂林
  • 17,050
  • 29
  • 92
  • 146
  • I think then you'll have to do the parsing yourself (or use some library for it.) – Pekka Dec 04 '10 at 15:16
  • possible duplicate of [Creating a new DOM element from an HTML string using built-in DOM methods or prototype](http://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro) – Dan Dascalescu Feb 04 '14 at 08:37

3 Answers3

48

You can create some dummy outer element:

  var div = document.createElement('DIV');

and then:

  div.innerHTML = '<table class="list"><tr><td><a href="xxx">title</a></td></tr></table>'

and then extract it from childNodes:

  div.firstChild

innerHTML is a Microsoft extension, but one universally supported on all modern browsers.

Of course you can form a simple function which does what you want from these snippets.

EFraim
  • 12,811
  • 4
  • 46
  • 62
  • Yes, you can reuse it. (replacing innerHTML will release the previous children and instantiate new ones) – EFraim Dec 04 '10 at 16:59
  • 1
    Beware, this would not work if the original string had multiple elements at the top level (one after the other). In that case we would have to loop through all the childNodes of the dummy div. – pasqal Feb 23 '22 at 16:24
  • 1
    @pasqal Good call, and it's even worse than that. If the string is `"

    some content

    "` (note the space!) then `el.firstChild` is a text node, not the `

    `.

    – ggorlen Mar 14 '22 at 22:29
3

Use caution with the top answer as pasqal suggests.

The problem is that .firstChild might not always be what you expect. In my case, I had a single root node (so I thought), but with leading whitespace. Here's a minimal example of my failing code:

const el = document.createElement("div");
el.innerHTML = `
  <p>foo</p>
`;
el.firstChild.classList.add("foo");

The issue is that .firstChild is really a .nodeType === Node.TEXT_NODE:

const el = document.createElement("div");
el.innerHTML = `
  <p>foo</p>
`;
console.log(el.childNodes[0].nodeType === Node.TEXT_NODE);
console.log(el.childNodes[0].textContent);
console.log(el.childNodes[1].nodeType === Node.TEXT_NODE);
console.log(el.childNodes[1].textContent);

How to deal with this is use-case dependent, but you could glue together the non-text nodes with .filter or if you're sure you have one root, as was my case, you can use .find:

const el = document.createElement("div");
el.innerHTML = `
  <p>foo</p>
`;
const root = [...el.childNodes].find(e => e.nodeType !== Node.TEXT_NODE)
console.log(root);
root.classList.add("foo"); // works now
ggorlen
  • 44,755
  • 7
  • 76
  • 106
0

You can also append an element using the parent's innerHTML property like this:

        this.parentEl.innerHTML += `
<a-sphere 
  class="a-sim-body"
  dynamic-body 
  position="1 1 1" 
  radius="1" 
  color="blue">
</a-sphere>`
Michael Cole
  • 15,473
  • 7
  • 79
  • 96
  • 1
    This will break event listeners for nodes already in the parent element as they will be re-created. It will as well cause images to re-render, etc. – undefined Apr 25 '21 at 11:57