Working with SVG from JavaScript

6 min. read

Caution! This article is 8 years old. It may be obsolete or show old techniques. It may also still be relevant, and you may find it useful! So it has been marked as deprecated, just in case.

For a while I've been trying to do a web based tic-tac-toe game just so that I could explore what it is like to manipulate the DOM of SVG sprites from JavaScript.

I finally got to it, although the project is still a work in progress.

The basic difference with HTML DOM manipulation seems to be the fact that you have to provide a namespace, but also there are some SVG specific methods. This is what I learnt:

SVG DOM manipulation

Getting elements by id

You can still use the basic getElementById(id):


var svg = document.getElementById(index);

but the inline SVG needs to have the xmlns attribute set:


<svg id="cell-1" viewBox="0 0 270 270" xmlns="http://www.w3.org/2000/svg">
  <!-- SVG code here -->
</svg>

Getting elements by tag name

There is a specific method for that, getElementsByTagNameNS(), and you need to pass the namespace.

For example, this code gets the <use> element so that we can set its value. The <use> element takes nodes from within the SVG document, and duplicates them somewhere else, reducing code duplication inside of the SVG sprite.

Setting the value of an attribute requires a specific method too: setAttributeNS(), and it also needs a namespace.


var BoardView = {
  svgNS: 'http://www.w3.org/2000/svg',
  linkNS: 'http://www.w3.org/1999/xlink'
};

var use = svg.getElementsByTagNameNS(BoardView.svgNS, 'use')[0];
use.setAttributeNS(BoardView.linkNS, 'href', '#' + mark);

If you are using sprites, then you need the xmlns:link in the inline SVG:


<svg id="cell-1" viewBox="0 0 270 270" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <use href="#sprite-id"></use>
</svg>

Creating SVGs in the DOM

Since this is an app that runs in the browser, I am using the Jasmine standalone library to run the tests against the browser directly. That way I can do manual cross-browser testing (you can automate it using any of the many services out there for a sum of money, but hey, this is a pet project).

You can create elements from JavaScript to setup your test fixtures like this:


function createCell(index, mark) {
  var cell = document.createElementNS(svgNS, 'svg');
  cell.setAttributeNS(svgNS, 'xmlns', svgNS);
  cell.setAttributeNS(linkNS, 'xmlns:xlink', linkNS);
  cell.setAttributeNS(svgNS, 'id', index);

  var use = document.createElementNS(linkNS, 'use');
  use.setAttributeNS(linkNS, 'href', '#' + mark);

  cell.appendChild(use);
}

function insertCell(index, mark) {
  var body = document.body;
  body.insertBefore(cell, body.firstChild);
  return cell;
}

If you prefer, since tests are a controlled environment and the code won't go to production and won't receive external input (you are literally setting the input) you could use element.innerHTML:


function insertCell(index, mark) {
  board = document.getElementById(boardId);
  board.innerHTML = '<svg id="' + index + '" viewBox="0 0 270 270" xmlns="' + svgNS + '" xmlns:xlink="' + linkNS + '"><use href="#' + mark + '"></use></svg>';
}

function removeCell() {
  board.innerHTML = '';
}

You can also use the DOMParser, which is experimental at the moment of writing this, but looks promising.

Just bear in mind that SVG is a different namespace and it doesn't define the same setters and getters:

Alternatives

You can also use an object tag to embed an inline SVG in the HTML DOM so that it works with Internet Explorer:


<object type="image/svg+xml" data="mySVG.svg">
  <img src="fallback-image.png" alt="…" />
</object>

You could also use these cool techniques by Sara Soueidan but then you can not use SVG sprites.

Also, if your sprites are small, it's better to keep them in the same document, because IE doesn't support linking to external SVG in a link inside an SVG.

To search elements inside an inline SVG, you could use XPath, but IE doesnt support it and can I use doesn't know about it at the moment of writing this.

Comments