SVG Scripting
You can read and rewrite any SVG element from JavaScript with document.getElementById and setAttribute, exactly the same way you script an HTML element.
SVG Lives in the DOM Too
When SVG markup is inline in an HTML page, every element - <svg>, <circle>, <path>, <g> - becomes a real node in the same DOM tree as your HTML. That means the same methods you use on <div> or <button> work on SVG shapes: document.getElementById, querySelector, addEventListener, getAttribute, and setAttribute. There is no separate SVG-only API you need to learn just to start manipulating shapes.
Selecting Elements and Reading Attributes
Give the shapes you want to control an id, then fetch them with document.getElementById("id") just like an HTML element. getAttribute("name") returns the current value as a string - even numeric attributes like r or cx come back as text, so wrap them in Number() or parseFloat() before doing arithmetic with them.
Example
<!DOCTYPE html>
<html>
<body>
<svg viewBox="0 0 120 120">
<circle id="dot" cx="60" cy="60" r="20" fill="#e74c3c" />
</svg>
<script>
const circle = document.getElementById("dot");
const radius = Number(circle.getAttribute("r"));
console.log(`current radius: ${radius}`);
</script>
</body>
</html>Changing Shapes with setAttribute
setAttribute(name, value) writes a new value onto an element, and the browser repaints it immediately. This works for geometry (cx, cy, r, x, y, width, height), presentation (fill, stroke, opacity), and even the transform attribute, though for transform you typically build the whole string yourself, e.g. setAttribute("transform", `translate(${x}, ${y})`).
Example
<!DOCTYPE html>
<html>
<body>
<svg viewBox="0 0 200 100">
<rect id="box" x="20" y="30" width="60" height="40" fill="#e74c3c" />
</svg>
<script>
const box = document.getElementById("box");
let isRed = true;
box.addEventListener("click", () => {
isRed = !isRed;
box.setAttribute("fill", isRed ? "#e74c3c" : "#2980b9");
box.setAttribute("x", isRed ? "20" : "120");
});
</script>
</body>
</html>Custom Animation with requestAnimationFrame
For animation logic that a declarative <animate> can't express - physics, easing tied to user input, or values computed at runtime - drive setAttribute from a requestAnimationFrame loop instead. Each frame, compute the next value, write it with setAttribute, and schedule the next frame; call cancelAnimationFrame with the stored id to stop it.
Example
<!DOCTYPE html>
<html>
<body>
<svg viewBox="0 0 200 40">
<circle id="ball" cx="10" cy="20" r="8" fill="#27ae60" />
</svg>
<script>
const ball = document.getElementById("ball");
let x = 10;
let direction = 1;
let frameId;
function step() {
x += direction * 2;
if (x > 180 || x < 10) direction *= -1;
ball.setAttribute("cx", x);
frameId = requestAnimationFrame(step);
}
step();
// cancelAnimationFrame(frameId) to stop
</script>
</body>
</html>- To create brand-new SVG elements at runtime, use document.createElementNS("http://www.w3.org/2000/svg", "circle") - plain createElement() produces an HTML element that won't render inside <svg>.
- Camel-cased attributes like viewBox and preserveAspectRatio must be spelled with the exact SVG casing when passed to setAttribute; unlike HTML, SVG attribute names are case-sensitive.
- element.getBBox() returns the element's untransformed bounding box in its own coordinate system, handy for centering a rotation or aligning shapes programmatically.
- Event listeners, classList, and dataset all work on SVG elements exactly as they do on HTML elements.
- Reading layout-affecting attributes and writing them in the same loop iteration can cause layout thrashing; batch reads before writes when animating many elements at once.
Exercise: SVG Scripting
Why must you use document.createElementNS() instead of document.createElement() to dynamically create an SVG <circle>?