Drew Noakes
Drew Noakes

Reputation: 310917

Succinct construction of SVG nodes in JavaScript

Consider the following chunk of SVG:

<defs>
  <radialGradient id="gradient" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
    <stop offset="0%" style="stop-color:#568f82;stop-opacity:1" />
    <stop offset="100%" style="stop-color:#568f82;stop-opacity:0" />
  </radialGradient>
</defs>

The corresponding JavaScript to create those elements is:

var defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
var radialGradient = document.createElementNS('http://www.w3.org/2000/svg', 'marker');
radialGradient.id = 'gradient';
radialGradient.setAttribute('cx', '50%');
radialGradient.setAttribute('cy', '50%');
radialGradient.setAttribute('fx', '50%');
radialGradient.setAttribute('fy', '50%');
radialGradient.setAttribute('r', '50%');
var stop1 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
stop1.setAttribute('offset', '0%');
stop1.setAttribute('style', 'stop-color:#568f82;stop-opacity:1');
radialGradient.appendChild(stop1);
var stop2 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
stop2.setAttribute('offset', '100%');
stop2.setAttribute('style', 'stop-color:#568f82;stop-opacity:0');
radialGradient.appendChild(stop2);
defs.appendChild(radialGradient);
this.svgElement.appendChild(defs);

Is there a way of using the XML markup inline in the JavaScript? I've tried something resembling the following but without any luck:

var defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
defs.innerHTML = 
    '<radialGradient id="gradient" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">' +
    '    <stop offset="0%" style="stop-color:#568f82;stop-opacity:1" />' +
    '    <stop offset="100%" style="stop-color:#568f82;stop-opacity:0" />' +
    '</radialGradient>';

It seems that innerHTML is not a property of SVGElement.

Upvotes: 0

Views: 187

Answers (3)

thSoft
thSoft

Reputation: 22660

If you want even more concise syntax and you are willing to use libraries, consider trying Raphaël or svg.js.

Upvotes: 0

Robert Longson
Robert Longson

Reputation: 124059

var parser = new DOMParser();
var doc = parser.parseFromString(stringContainingXMLSource, "image/svg+xml");

Note that the parent node will need to have an explicit namespace so that it gets parsed correctly i.e. the String would need to look like this:

var stringContainingXMLSource = '<radialGradient xmlns="http://www.w3.org/2000/svg" id="gradient" ...

Then you can do

defs.appendChild(document.importNode(doc.documentElement));

Some UAs also allow doc.rootElement instead of doc.documentElement but apparently not all.

Upvotes: 2

bobince
bobince

Reputation: 536399

There isn't a direct markup property, but you can use DOMParser.parseFromString() to parse some XML into a new SVG document and importNode it across. Or, you can use innerHTML to set the content of a container HTML element and pull the SVG nodes out of the result.

But string-slinging gets a bit mucky—as soon as you want to bring in variable content or attribute values, it's easy to do a string concatenation without remembering proper escaping, leading to cross-site-scripting problems. It's nice to be able to keep everything in a world of objects and properties rather than encoded markup when you can.

Admittedly the uber-verbose DOM API doesn't make that easy, but you can write shortcut functions to call the DOM methods for you, allowing you to write something like:

xml(SVG, 'defs', {}, [
    xml(SVG, 'radialGradient', {id: 'gradient', cx: '50%', cy: '50%', fx: '50%', fy: '50%' r: '50%'}, [
        xml(SVG, 'path', {offset:   '0%', 'stop-color': '#568f82', 'stop-opacity': 1}),
        xml(SVG, 'path', {offset: '100%', 'stop-color': '#568f82', 'stop-opacity': 0})
    ])
])

which is a lot easier to cope with.

Upvotes: 1

Related Questions