Append a Child to an SVG Element in Pure JavaScript

Why write a blog post about this, Gavin? Well it turns out that appending a child to an SVG, or within an SVG group, isn’t as simple as it would be if we were working with plain HTML.

I’m sure there are good reasons for this, but part of me would really like to know what those reasons are! I imagine they will be something to do with SVGs being written in XML rather than HTML. That, however, is research for another day, unless someone reading this knows the answer in which case all comments are welcome.

Well, why not just use jQuery or D3? They both have an append function and it works just fine.

They do indeed, however, for this particular project I want something that ships as light as possible, and as light and as powerful as jQuery and D3 are, it would still mean downloading an entire library just for the sake of appending elements to an SVG, whereas this entire project (so far) has 21 lines of JavaScript.

Fear not though, because if you’ve ever tried the below code:

let newElement = document.createElement('rect');
newElement.setAttribute('fill','orange');
newElement.setAttribute('width','200');
newElement.setAttribute('height','200');
document.getElementById('svg-drawing').appendChild(newElement);

Only for nothing to appear, despite it appearing in the DOM (which really does call into question what those good reasons could possibly be), then look no further, I have the solution.

To really work properly with SVGs we have to enter the world of namespaces. Indeed, this is exactly what jQuery and D3 do. By making one small change to our code we can make it work as it should, observe below:

let newElement = document.createElementNS('http://www.w3.org/2000/svg','rect');
newElement.setAttribute('fill','orange');
newElement.setAttribute('width','200');
newElement.setAttribute('height','200');
document.getElementById('svg-drawing').appendChild(newElement);

Now, the differences under the hood between createElementNS and createElement, I couldn’t tell you, though it’s clear that it somehow tells the browser it is creating an SVG rectangle element, rather than just a rectangle element to go, um, somewhere else, somehow, I guess.

And that’s basically it! I’ve taken the liberty of writing a helpful function to append on object to SVG, as I’m sure you won’t want to be typing, or even copying and pasting, that URL each time.

const appendSVGChild = (element,target,attributes = {},text = '') => {
  let e = document.createElementNS('http://www.w3.org/2000/svg',element);
  Object.entries(attributes).map(a => e.setAttribute(a[0],a[1]));
  if (text) {
    e.textContent = text;
  }
  target.appendChild(e);
  return e;
};

// If applying attributes, they need to be in the format {'attr':'val','attr2':'val2',...}

Thoughts? Comments? Improvements? Improvements especially welcome as I’m sure there are some to be made!

Cross-posted on Dev.to