Using Javascript with SVG


April 26, 2018 Code on Github

Introduction

[This is a completely rewritten version of a post from March 29, 2013]

Like HTML, SVGs are represented using the Document Object Model (DOM) and so can be manipulated with Javascript relatively easily, especially if you are familiar with using JS with HTML. All the code examples can be found by following the Github link at the top of this post.

Multiple methods

I'm going to cover how to work with two types of SVGs:

  • Inline
  • External (added using <embed>, <object> or <iframe> tag)

As mentioned it my basic SVG tutorial, it's also possible to add SVGs using the <img> tag and as a background-image in CSS, but neither of these methods allow you to manipulate the SVG with JS (or CSS). I'll also cover using JS

  • Inside the SVG itself
  • External to the SVG (within the HTML document or as a separate file altogether).

Most commonly, you'll probably want to use inline SVGs with external JS. This lets you to have separation of concerns, and easily reuse the JS for multiple SVGs on a website. However, you might already have the SVGs files, perhaps from Inkscape or Illustrator, in which case it can be useful to embed them. I sometimes like to have the JS embedded into external SVG files, so all the code is wrapped up together and can be emailed as a single file so the recipient can just open the SVG in their browser and have it work.

Getting the SVG document

If you using an external SVG or you want to create a new element within a SVG, then you will first need to get the SVG document. If you are just getting or setting the attributes of an inline SVG, then you can skip this section.

Below are two SVGs, one inline and one external, added with an <object> tag. The inline SVG has an id of "inline-1", the external SVG has an id of "external-1", and the object has an id of "svg-object".

<object id="svg-object" data="path/to/external.svg" type="image/svg+xml"></object>
Inline SVG

Note that they look different because the inline SVG is styled by the CSS from this page. Also, if you right click on the external SVG you should have an additional option to view its source.

Inline SVG

To get an inline SVG element, you can use document.getElementById() regardless of whether the script tag is inside the SVG or not (and you might as well separate it from the SVG).

<script type="text/javascript">
    var svg = document.getElementById('inline-1');
    console.log(svg);
</script>

External SVG + Internal JS

For an external SVG, you can use the same code when adding the <script> element into the SVG itself. However, you may want to wrap the code with CDATA. If you don't, then the XML parse will consider the JS code part of XML, and if you use < or >, it will break (as in this example), thinking you're trying to start or end a tag.

This code will work despite containing 1 < 2, because of the <![CDATA[ ... ]]> wrapping.

<script type="text/javascript"><![CDATA[
    var svg = document.getElementById('external-1');
    console.log(svg);
    console.log(1 < 2);
]]></script>

In this case we could also use:

var svg = document.getElementsByTagName('svg')[0];

We can do this because the SVG cannot access the HTML document it's embedded into and so cannot "see" the other SVGs on the page.

External SVG + External JS

In this case we can't access the SVG element directly as it's hidden inside the <object> element. So first, we have to get the object and then access its contentDocument. Once we have the SVG document, we can continue as before.

However, there is also an issue that the SVG within the object might not have loaded by the time we reach the script element (which I'm assuming you've added to the end of the HTML document). So we have to make sure we don't try to get the element before the HTML window has loaded.

You would also need to do this if you we using an inline SVG and either had the <script> element before it.

<script type="text/javascript">
  window.addEventListener("load", function() {
    var svgObject = document.getElementById('svg-object').contentDocument;
    var svg = svgObject.getElementById('external-1');
    console.log(svg);
  });
</script>

Getting an element

Now we know how to get the SVG document, let's get an element from inside the SVG with an id of "item".

To get an element from an inline SVG, you can use:

var element = document.getElementById('item');

To get an element from an external SVG, first get the SVG object as before, and then get the element using the SVG object's getElementById() method:

window.addEventListener("load", function() {
  var svgObject = document.getElementById('svg-object').contentDocument;
  var element = svgObject.getElementById('item');
});

You can also use the getElementsByTagName() or getElementsByClassName() methods, but remember these will return collections of items, not just one.

Getting and setting attributes

Once you have selected an element, you can get and set its attributes with getAttributeNS() and setAttributeNS(). For example, we can get an element's y coordinate and add 10 to it like so:

var y = parseFloat(element.getAttributeNS(null, 'y'));
element.setAttributeNS(null, 'y', y + 10);

This will work regardless how the SVG is added to the page or where the JS is.

Note that these functions are slightly different from the standard getAttribute and setAttribute methods because the elements are not HTML elements, rather in the SVG namespace (NS stands for namespace). All this means is that you have to pass an additional parameter to each method, which can just be null.

Inline SVG

Now the text elements have been moved down. Something else to note is that both the text elements have an id of "item", which works because they are not in the same document.

Creating and removing elements

To create elements, you need to use the relevant document's createElementNS() method, passing in the SVG namespace and the tag name. You then will most likely want to add it to the SVG element with appendChild(). You can also use the insertBefore() or insertAfter() methods of an SVG element.

Whatever method you use, so long as you have got the svg element, you can use:

var element = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
svg.appendChild(element);
Inline SVG

Creating text

Creating a text element that contains text is the same as it is in HTML: you have to create a separate text node using createTextNode(). You then append this to a text element. For example:

var element = document.createElementNS('http://www.w3.org/2000/svg', 'text');
element.setAttributeNS(null, 'x', 5);
element.setAttributeNS(null, 'y', 15);
var txt = document.createTextNode("Hello World");
element.appendChild(txt);
svg.appendChild(element);

Removing an element

Removing an element is the same as it is in HTML. Get the element by id (or however), and then pass it into:

var element = document.getElementById('item');
svg.removeChild(element);

Simple example

This is a simple example, using sliders to change the cx and cy attributes of a circle element.

var moveSlider = function(slider, direction) {
    var value = slider.value;
    var circle = document.getElementById("target");
    var coord = "c" + direction;
    circle.setAttributeNS(null, coord, value * 5);
}
:
:

This function is called whenever the sliders are changed with the oninput event:

<input type="range" min="1" max="60" value="20" oninput="moveSlider(this, 'x')"/>
 <input type="range" min="1" max="40" value="10" oninput="moveSlider(this, 'y')"/>

Comments (8)

Richard Smith on Aug. 17, 2013, 11:11 a.m.

You should check out D3.js, which is the canonical way to manupulate SVG with JS.

Peter on Aug. 18, 2013, 1:15 p.m.

Thanks Richard. I have looked into D3.js a bit and it is quite cool for graphs, but I find it can be a bit limiting.

Anonymous on Nov. 28, 2013, 7:26 p.m.

Brilliant simple. I've searching hours on the internet before I've found your exemple that enlightened me. Thank you very much!

Sorin on Nov. 28, 2013, 10:37 p.m.

Hi,

When I put the variable outside the functions but into JavaScript, the script doesn't work. It seems that it doesn't like the global variables. What is to do?

For example, the following script doesn't work

<script type='text/javascript'>
var svg = document.getElementById("circle-svg");
var svgDoc = svg.contentDocument;

var moveSlider = function(slider, direction) {
var circle1 = svgDoc.getElementById("my-circle");
var value = slider.value;
circle1.setAttributeNS(null, "c" + direction, value * 5);
}

But that script works:

<script type='text/javascript'>
var moveSlider = function(slider, direction) {
var svg = document.getElementById("circle-svg");
var svgDoc = svg.contentDocument;
var circle1 = svgDoc.getElementById("my-circle");

var value = slider.value;
circle1.setAttributeNS(null, "c" + direction, value * 5);
}

Why's that?

Serge on April 15, 2014, 3:37 p.m.

My way: http://svgmnemo.ru/pub/svgdyn.html, but on the Russian, sorry.

Adam on Sept. 6, 2018, 12:13 p.m.

I'd like like to render an SVG of my office floor plan and then allow users to search for a person using search bar or drop down menu then have the persons desk change colour to show where they sit. is this possible with Javascript? I'm a bit of a novice at the minute with big idea's ;o)

Peter on Sept. 6, 2018, 8:38 p.m.

Yep, that's definitely possible. Give all the desks an id, then select by that id when the dropdown is chosen, then add a class to that element to highlight it. You'll also have to remove the highlight when you select a different desk. I might be able to create a prototype this weekend if I get time.

Mikey on Sept. 21, 2018, 12:52 p.m.

Peter, thank you for these tutorials. Really, really helpful because they are clear and cover so much.

Leave a comment

cancel reply

Tags

Javascript svg