The SVG DOM and namespaces
SVG is not HTML. While SVG embedded inline in a page shares the same DOM, its elements live in the XML namespace http://www.w3.org/2000/svg. That distinction matters the moment you try to create an SVG element from JavaScript.
If you write document.createElement('circle'), the browser creates an unknown HTML element — a valid DOM node, but one the browser has no idea how to render as a shape. It won't appear on screen. The correct call is:
Once you have a properly namespaced element, set its attributes with setAttribute. For the special case of href used in <use> or <image>, use setAttributeNS(null, 'href', '...') or the XLink namespace, but plain setAttribute is fine for everything else (cx, cy, r, fill, etc.).
Demo: Add a circle
Each click on "Add circle" calls createElementNS, sets random cx/cy/r/fill attributes, and appends the element to the inline SVG. Open DevTools and inspect the resulting elements — every one will report the correct SVG namespace.
Manipulating existing SVG elements
Once you can reach an SVG element — via getElementById, querySelectorAll, or by traversing svg.children — you can read and write any attribute using the same getAttribute / setAttribute API that works on HTML elements. SVG elements are first-class DOM nodes.
A few things to be aware of: SVG presentations attributes (fill, stroke, opacity, etc.) can be set either as attributes or as CSS properties. Attribute changes via setAttribute are always visible from JavaScript. CSS style declarations take precedence over presentation attributes, so if you set fill via setAttribute but a stylesheet rule overrides it, the shape won't change — in that case use element.style.fill = '...' instead.
Demo: Cycle fills and grow radius
The buttons below iterate over a row of pre-existing circles in the SVG. "Next color" advances their fill attribute to the next palette entry. The range input sets the r attribute on all circles simultaneously. Both operations use only querySelectorAll and setAttribute — no frameworks required.
Events on SVG shapes
SVG elements are DOM nodes, so they support the full set of browser events: click, pointerover, pointerout, focus, and so on. You attach handlers with addEventListener exactly as you would on a <button> or <div>.
The event.target inside the handler is the exact SVG element that was interacted with. Because SVG elements participate in the browser's event-bubbling model, you can also use event delegation: attach a single listener to the parent <svg> and check event.target inside it — a common pattern when the number of shapes is large or dynamically changing.
Demo: Click a shape to identify it
Each shape below has a data-name attribute. A single delegated click listener on the <svg> reads that attribute and writes it to an <output> element. Keyboard users can tab to the shapes (they have tabindex="0") and press Enter to trigger the same handler.
Click a shape...
Pointer coordinates in SVG user units
When a pointer event fires, event.clientX and event.clientY give you the position in CSS pixels relative to the viewport. But SVG shapes live in SVG user units, defined by the viewBox. If your SVG is scaled, panned, or rotated (by CSS transforms or by the viewBox itself), a 1:1 mapping between pixels and SVG units no longer holds.
The correct approach uses the SVG's current transformation matrix (CTM) — the combined effect of all transforms between the viewport and the SVG coordinate system:
A common shortcut is getBoundingClientRect(): subtract the SVG's bounding-box origin and scale by viewBox / boundingRect. That works when the SVG has no CSS transforms applied to it or its ancestors, but silently breaks if the SVG is inside a transform: scale() or a CSS zoom context. The getScreenCTM() approach is correct in all cases.
Demo: Click to drop a dot
Click anywhere inside the SVG below. The script converts the mouse position to SVG user units with getScreenCTM().inverse() and places a small circle exactly where you clicked. The crosshair cursor makes the hit area obvious.
Click the canvas to drop a dot...
Inline SVG vs. <img src="…svg">
All the techniques above — createElementNS, setAttribute, addEventListener, getScreenCTM — require the SVG to be inline in the HTML document (or loaded into the same document origin via XMLHttpRequest/fetch and injected as DOM nodes). When an SVG is loaded as an external resource via <img src="graphic.svg">, the browser renders it as a replaced element in a separate browsing context. You have no DOM access, no event listeners on individual shapes, and no JavaScript at all runs inside it.
The same restriction applies to SVG in a CSS background-image. If your SVG needs to be interactive or driven by JavaScript, it must be inline. If it is purely decorative, an <img> with a good alt attribute is simpler and benefits from browser caching.