Scalable vector graphics (SVG) is a part of the document object model (DOM) and thus can be modified just like any other DOM node from JavaScript. But SVG has some pitfalls like having its own coordinate system and different style attributes which can be a headache. What follows is a non comprehensive list of hints and tricks which I found helpful while working with SVG.
Coordinate system
From screen coordinates to SVG
function screenToSVG(svg, x, y) { // svg is the svg DOM node var pt = svg.createSVGPoint(); pt.x = x; pt.y = y; var cursorPt = pt.matrixTransform(svg.getScreenCTM().inverse()); return {x: Math.floor(cursorPt.x), y: Math.floor(cursorPt.y)} }
From SVG coordinates to screen
function svgToScreen(element) { var rect = element.getBoundingClientRect(); return {x: rect.left, y: rect.top, width: rect.width, height: rect.height}; }
Zooming and panning
Getting the view box
function viewBox(svg) { var box = svg.getAttribute('viewBox'); return {x: parseInt(box.split(' ')[0], 10), y: parseInt(box.split(' ')[1], 10), width: parseInt(box.split(' ')[2], 10), height: parseInt(box.split(' ')[3], 10)}; };
Zooming using the view box
function zoom(svg, initialBox, factor) { svg.setAttribute('viewBox', initialBox.x + ' ' + initialBox.y + ' ' + initialBox.width / factor + ' ' + initialBox.height / factor); } function zoomFactor(svg) { var height = parseInt(svg.getAttribute('height').substring(0, svg.getAttribute('height').length - 2), 10); return 1.0 * viewBox(svg).height / height; }
Panning (with zoom factor support)
function pan(svg, panX, panY) { var pos = viewBox(svg); var factor = zoomFactor(svg); svg.setAttribute('viewBox', (pos.x - factor * panX) + ' ' + (pos.y - factor * panY) + ' ' + pos.width + ' ' + pos.height); }
Misc
Embedding HTML
function svgEmbedHTML(width, height, html) { var svg = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject"); svg.setAttribute('width', '' + width); svg.setAttribute('height', '' + height); var body = document.createElementNS('http://www.w3.org/1999/xhtml', 'body'); body.style.background = 'none'; svg.appendChild(body); body.appendChild(html); return svg; }
Making an invisible rectangular click/touch area
function addTouchBackground(svgRoot) { var rect = svgRect(0, 0, '100%', '100%'); rect.style.fillOpacity = 0.01; root.appendChild(rect); }
Using groups as layers
This one needs an explanation. The render order of the svg children depends on the order in the DOM: the last one in the DOM is rendered last and thus shows above all others. If you want to have certain elements below or above others I found it helpful to use groups in svg and add to them.
function svgGroup(id) { var group = document.createElementNS('http://www.w3.org/2000/svg', 'g'); if (id) { group.setAttribute('id', id); } return group; } // and later on: document.getElementById(id).appendChild(yourElement);