Article
0 comment

Multiple ways to insert HTML or SVG Elements into Document Object Model (DOM)

Show multiple ways to insert HTML Elements to the DOM

There are some rear occasions after working on how things work for a long time on the web. One of these occasions happened last week.

I tried to insert an SVG graphic into a div container. While there are multiple ways to do it, only innerHTML rendered the SVG illustration properly. Why is this so?

innerHTML is not a property

innerHTML is not a property. It’s a function call. Sound strange because you assign a new HTML to the existing DOM.

So what happens when you assign a new HTML string to innerHTML, it calls the browser-internal serialisation engine before the content gets updated.

Value to set
A string containing the HTML serialisation of the element’s descendants. Setting the value of innerHTML removes all of the element’s descendants and replaces them with nodes constructed by parsing the HTML given in the string htmlString.
Source: MDN

Setting innerHTML comes with security risks because the content is serialised but not sanitised. So you cannot remote, for example, script tags from user input as such during the innerHTML. You have to deal with it manually.

In future you should be able to use SetHTML. While marked as experimental, it works on Edge and Chrome (v103), but there might not be a sanitiser in place that removes troublesome content.

My favourite: insertAdjacentHTML, insertAdjacentElement, insertAdjacentText

These three methods are personally my favourite methods to add new DOM elements. These allow you to control where you add your HTML universally.

<!-- beforebegin -->
<p class="helloworld">
  <!-- afterbegin -->
  foo
  <!-- beforeend -->
</p>
<!-- afterend -->

‘beforebegin’, ‘afterbegin’, ‘beforeend’, ‘afterend’ are the options where to insert your HTML. Append a new HTML element, for example would look like this.

let helloWorld = document.querySelector('.helloWorld');

// insert before the closeing tag
helloWorld.insertAdjacentHTML('beforeend', '<strong>The End</strong>');

So after doing this, you will get the following HTML result.

<!-- beforebegin -->
<p class="helloworld">
  <!-- afterbegin -->
  foo
  <strong>The End</strong>
  <!-- beforeend -->
</p>
<!-- afterend -->

The only downside of this method is that it only parses the HTML before inserting it into the DOM. Many people consider it to be more performant.

In the case of an SVG Graphic, there is a need to parse the content and serialise the content. It may lead to an invalid SVG or at least haven’t shown the expected outcome.

append, appendChild, prepend

Finally, these methods allow you also to insert Elements in the DOM. Well, not quite because technically, it does not insert HTML Elements. It insert a Node.

The DOM Node interface is an abstract base class upon which many other DOM API objects are based, thus letting those object types be used similarly and often interchangeably. As an abstract class, there is no such thing as a plain Node object. All objects that implement Node functionality are based on one of its subclasses. Most notable are Document, Element, and DocumentFragment.
Source: MDN

So Node is the base class from which Document, Element and Document Fragment derives. This base class and the methods have a caveat: when you append a Node or a Child, no serialisation happens, and it will be a Node into the M DOM.

So it has the same impact as the insertAdjacent*. No parsing and serialisation happen, and in the case of SVG, not all of its code gets rendered correctly.

Conclusion

While all the other methods work in regular cases pretty well as long as it concerns HTML, with SVG documents, you only have the option to insert them via innerHTML, which comes with some security risks attached.

The innerHTML function might also cast errors, such as SyntaxError or NoModificationAllowedError. Sadly I haven’t been able to produce those errors.

I guess before adding something via innerHTML, I would recommend parsing the string using DOMParser.parseFromString(). This way, you can even make sure the content is correct.

For SVG this looks like this:

const parser = new DOMParser();

const svgString = "<circle cx=\"50\" cy=\"50\" r=\"50\"/>";
const doc2 = parser.parseFromString(svgString, "image/svg+xml");

This way, you can even handle errors in your string.

const parser = new DOMParser();

const xmlString = "<warning>Beware of the missing closing tag";
const doc = parser.parseFromString(xmlString, "application/xml");
const errorNode = doc.querySelector('parsererror');
if (errorNode) {
  // parsing failed
} else {
  // parsing succeeded
}

All in all, innerHTML is not a property but rather a function.

Leave a Reply

Required fields are marked *.


This site uses Akismet to reduce spam. Learn how your comment data is processed.