Appending a Child to an SVG using Pure Javascript
Tags: appendchild, vanilla, javascript, svg
Published: 2019-12-30 22:04:12

**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. I see an increasing number of answers on Stack Overflow these days saying "just use jQuery or D3", and the response from the OP being "that's not in the spec of the project".

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

```javascript
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:

```javascript
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 an object to an SVG, as I'm sure you won't want to be typing, or even copying and pasting, that URL each time.

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

// 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!

**UPDATE** As I now use TypeScript wherever possible, I have taken the liberty of rewriting the function in TypeScript below:

```typescript
function appendSVGChild(elementType: string, target: HTMLElement | SVGElement,attributes: Record<string, unknown> = {}, text = '') {
  const element: SVGElement = document.createElementNS('http://www.w3.org/2000/svg',elementType);
  Object.entries(attributes).map(a => element.setAttribute(a[0],a[1] as string));
  if (text) {
    const textNode = document.createTextNode(text);
    element.appendChild(textNode);
  }
  target.appendChild(element);
  return element;
};

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