Create SVG elements with document.createElementNS('http://www.w3.org/2000/svg 189 ', name); plain createElement makes an unknown HTML element that never draws. Every shape receives pointer and keyboard events, and hit testing follows paint: under the default pointer-events: visiblePainted, a click lands on a shape only where its fill or stroke is painted. The property also takes fill, stroke, all, none and bounding-box (Chrome 1 and Safari 10 only). To convert event coordinates to user units, apply the inverse of getScreenCTM() to a DOMPoint; the older createSVGPoint() is discouraged.
<svg id="board" width="400" height="200" viewBox="0 0 200 100">
<circle id="ring" cx="50" cy="50" r="35" fill="none" stroke="#1f5f8b" stroke-width="6"/>
<circle id="disc" cx="150" cy="50" r="35" fill="#e09a10"/>
<text x="150" y="55" text-anchor="middle" pointer-events="none">4.8</text>
</svg>
<script>
board.addEventListener('pointerdown', e => {
const p = new DOMPoint(e.clientX, e.clientY)
.matrixTransform(board.getScreenCTM().inverse());
console.log(`client (${e.clientX}, ${e.clientY}) -> user (${p.x}, ${p.y})`,
`hit #${e.target.id}`);
});
</script>client (108, 108) -> user (50, 50) hit #board client (38, 108) -> user (15, 50) hit #ring client (308, 116) -> user (150, 54) hit #disc
Playwright 26,280 clicked the ring's center, its stroke and the "4.8" label: the viewBox halves every distance, the ring's hole falls through to the <svg>, and the label passes its click to the disc. getBBox(), getTotalLength(), getPointAtLength() and isPointInFill() complete the toolkit.