Pointer Events and the SVG DOM

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.

Where a click lands, in client pixels and in viewBox unitsHTMLLive
<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>

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.