SVG and the DOM

Dr. Greg Bernstein

September 3, 2019

SVG and the DOM

HTML, SVG, and the DOM

Consider part of the file guitarCSS.html

<body>
    <h1>Guitar Fretboard Diagrams</h1>
    <p>Here we demonstrate a basic E major Guitar chord drawn
    with SVG and styled with CSS. </p>

    <svg width="300" height="200" xmlns="http://www.w3.org/2000/svg">
        <line class="GString"  x1="10" x2="10" y1="10" y2="170" />
        <line class="GString"  x1="30" x2="30" y1="10" y2="170" />
        <line class="GString"  x1="50" x2="50" y1="10" y2="170" />
        <line class="GString"  x1="70" x2="70" y1="10" y2="170" />
        <line class="GString"  x1="90" x2="90" y1="10" y2="170" />
        <line class="GString"  x1="110" x2="110" y1="10" y2="170" />

        <line class="GFret"  x1="10" x2="110" y1="10" y2="10" />
        <line class="GFret"  x1="10" x2="110" y1="50" y2="50" />
        <line class="GFret"  x1="10" x2="110" y1="90" y2="90" />
        <line class="GFret"  x1="10" x2="110" y1="130" y2="130" />
        <line class="GFret"  x1="10" x2="110" y1="170" y2="170" />

        <circle cx="30" cy="70" r="10" />
        <circle cx="50" cy="70" r="10" />
        <circle cx="70" cy="30" r="10" />
    </svg>
</body>

Try some DOM queries on the SVG

  • document.querySelectorAll('circle')
  • document.querySelectorAll('line.GString') with class

Results

Work just like HTML DOM queries!

SVG queries
SVG queries

SVG Element creation

SVG Elements are DOM Elements

From MDN SVGElement Reference

All of the SVG DOM interfaces that correspond directly to elements in the SVG language derive from the SVGElement interface.

SVG Element Creation

SVG Attributes

  • SVG usually have a lot of attributes to set. Use setAttribute()

  • Example:
square.setAttribute("x", x);
square.setAttribute("y", y);
square.setAttribute("width", width);
square.setAttribute("height", width);

Random Square Generation

Script within randomSquares.html

var mySVG = document.getElementById("MyDrawing");
var maxSize = 50,
  maxX = 500,
  maxY = 300;

function randomSquare() {
  let x = Math.random()*(maxX - maxSize);
  let y = Math.random()*(maxY - maxSize);
  let width = Math.random()*maxSize;
  let square = document.createElementNS("http://www.w3.org/2000/svg", "rect");
  square.setAttribute("x", x);
  square.setAttribute("y", y);
  square.setAttribute("width", width);
  square.setAttribute("height", width);
  let colorStr = `rgb(${255*Math.random()}, ${255*Math.random()}, ${255*Math.random()})`;
  square.setAttribute("fill", colorStr);
  square.setAttribute("fill-opacity", 0.7);
  return square;
}

for (let i = 0; i < 30; i++) {
  mySVG.appendChild(randomSquare());
}

Random Squares Rendered

SVG and DOM Events

SVG DOM Events

SVGElement inherits from EventTarget

So supports a wide variety of events.

Mouse Press Detection Example.

  • Make the Guitar Chord SVG interactive
  • A First Step towards some type of custom control component.
  • Listen for mousedown events

HTML and SVG

From guitarHitTest.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>SVG DOM Events</title>
    <style><!-- Not shown here --></style>
</head>
<body>
    <h1>Guitar Fretboard Diagrams</h1>
    <p>Here we demonstrate some basic SVG DOM events.</p>
    <p>You clicked: <span id="Xcoord"></span>, <span id="Ycoord"></span>,
        String: <span id="GString"></span>, Fret: <span id="Fret"></span>.</p>
    <svg id="GChord" width="300" height="200" xmlns="http://www.w3.org/2000/svg">
        <line class="GString" x1="10" x2="10" y1="10" y2="170" />
        <line class="GString" x1="30" x2="30" y1="10" y2="170" />
        <line class="GString" x1="50" x2="50" y1="10" y2="170" />
        <line class="GString" x1="70" x2="70" y1="10" y2="170" />
        <line class="GString" x1="90" x2="90" y1="10" y2="170" />
        <line class="GString" x1="110" x2="110" y1="10" y2="170" />
        <line class="GFret" x1="10" x2="110" y1="10" y2="10" />
        <line class="GFret" x1="10" x2="110" y1="50" y2="50" />
        <line class="GFret" x1="10" x2="110" y1="90" y2="90" />
        <line class="GFret" x1="10" x2="110" y1="130" y2="130" />
        <line class="GFret" x1="10" x2="110" y1="170" y2="170" />
        <circle cx="30" cy="70" r="10" />
        <circle cx="50" cy="70" r="10" />
        <circle cx="70" cy="30" r="10" />
    </svg>
    <script>// Shown later...   </script>
</body>
</html>

Desired Functionality

Want to know what string and fret the user clicks on:

Main Concepts

  • “Grab” items from DOM for manipulation or event registration, generally with ID or class information.

  • Define an event handling function. Here we also need to clean up some coordinates and such.

  • Register the event handling function with the relevant DOM element.

Code

var mySVG = document.getElementById("GChord"),
  myX = document.getElementById("Xcoord"),
  myY = document.getElementById("Ycoord"),
  myGString = document.getElementById("GString"),
  myFret = document.getElementById("Fret");
var rect = mySVG.getBoundingClientRect(); // For coordinate adjustment

function handleMouseClick(event) {
  console.log(event);
  let localX = event.clientX - rect.x;
  let localY = event.clientY - rect.y
  if (localX < 120) {
    let gstring = 6 - Math.round((localX - 10) / 20);
    myGString.innerHTML = String(gstring);
  }
  if (localY <= 170) {
    let fretNum = Math.round((localY+10)/40);
    myFret.innerHTML = String(fretNum);
  }
  myX.innerHTML = `x=${localX.toFixed(1)}`;
  myY.innerHTML = `y=${localY.toFixed(1)}`;
}

mySVG.addEventListener('mousedown', handleMouseClick);