about-svg.de

SVG mit JavaScript und dem DOM steuern

Teil 5: Scripting und Praxis · Kapitel 11 von 12 · Aktualisiert am · 3 Minuten Lesezeit

Jedes Element einer inline eingebetteten SVG-Grafik ist ein Knoten im DOM der Seite. Mit denselben Methoden, die du von HTML kennst, kannst du Formen ändern, neu zeichnen und auf Klicks reagieren.

Der Baum

Das Document Object Model beschreibt ein Dokument als Baum aus Knoten. Das <svg>-Element ist die Wurzel der Grafik, darunter hängen Gruppen, Formen und Texte. Skripte greifen auf diesen Baum zu, lesen Attribute aus und schreiben neue Werte hinein. Der Browser zeichnet nach jeder Änderung automatisch neu.

Wo das Skript steht, hängt von der Einbindung ab. Ist das SVG inline im HTML, schreibst du ganz normales JavaScript für die Seite. In einer eigenständigen SVG-Datei kann ein <script>-Element direkt im SVG stehen. Das funktioniert aber nur, wenn die Datei direkt oder über <object> geöffnet wird, nicht als <img>. Dort führen Browser aus Sicherheitsgründen keine Skripte aus.

Elemente finden

document.getElementById("kreis") liefert das Element mit der passenden ID. Für mehrere Elemente gibt es getElementsByTagName("rect") oder, flexibler, querySelectorAll("circle.aktiv") mit CSS-Selektoren. Ältere Anleitungen beginnen oft mit evt.target.ownerDocument oder getSVGDocument(), um an das Dokument zu kommen. Bei inline eingebettetem SVG ist das unnötig, weil Grafik und Seite dasselbe Dokument sind.

Attribute lesen und ändern

Mit getAttribute und setAttribute liest und setzt du jedes Attribut als Text. Das folgende Skript lässt ein Rechteck bei jedem Klick um zehn Einheiten breiter werden:

const balken = document.getElementById("balken");

balken.addEventListener("click", () => {
  const breite = Number(balken.getAttribute("width"));
  balken.setAttribute("width", breite + 10);
});

Gestaltung lässt sich auch über die style-Eigenschaft oder über Klassen ändern: balken.classList.add("markiert") mit einer passenden CSS-Regel ist meist sauberer als Farben im Skript. Für Animationen ohne Bibliothek eignet sich die Web Animations API mit element.animate(), die auch an SVG-Elementen funktioniert.

Neue Elemente erzeugen

Hier liegt der wichtigste Unterschied zu HTML. document.createElement("circle") erzeugt ein Element, das zwar so heißt, aber zum HTML-Namespace gehört und deshalb nicht gezeichnet wird. SVG-Elemente brauchen createElementNS mit dem SVG-Namespace als erstem Argument:

const NS = "http://www.w3.org/2000/svg";
const flaeche = document.getElementById("diagramm");

[40, 75, 55, 90].forEach((wert, i) => {
  const r = document.createElementNS(NS, "rect");
  r.setAttribute("x", 20 + i * 50);
  r.setAttribute("y", 110 - wert);
  r.setAttribute("width", 36);
  r.setAttribute("height", wert);
  flaeche.append(r);
});

Das Ergebnis dieses Skripts sieht so aus, hier als fertige Grafik ohne Skript eingebettet:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 230 120" width="230" height="120">
  <line x1="10" y1="110.5" x2="225" y2="110.5" stroke="#9aa3b2"/>
  <g fill="#1f5fd1">
    <rect x="20" y="70" width="36" height="40"/>
    <rect x="70" y="35" width="36" height="75"/>
    <rect x="120" y="55" width="36" height="55"/>
    <rect x="170" y="20" width="36" height="90" fill="#c2410c"/>
  </g>
</svg>
Vier Balken, wie sie das Skript oben aus dem Array [40, 75, 55, 90] erzeugt.

Die Attribute selbst setzt du wieder ohne Namespace mit setAttribute. Nur für alte xlink:href-Verweise braucht es setAttributeNS mit dem XLink-Namespace. Mit dem einfachen href aus SVG 2 entfällt auch das.

Messen: getBBox und getComputedTextLength

SVG-Elemente bieten eigene Methoden zum Vermessen. getBBox() gibt das umschließende Rechteck einer Form zurück, mit x, y, width und height im Koordinatensystem der Grafik. getComputedTextLength() liefert die Länge eines Textes. Beides ist praktisch, um Beschriftungen mit einem passenden Hintergrund zu versehen oder zu prüfen, ob ein Text in eine Fläche passt. Für Pfade gibt es getTotalLength() und getPointAtLength(), mit denen du die Länge einer Kurve oder einen Punkt darauf berechnest.

const label = document.querySelector("#beschriftung");
const kasten = label.getBBox();
const hinten = document.createElementNS(NS, "rect");
hinten.setAttribute("x", kasten.x - 4);
hinten.setAttribute("y", kasten.y - 2);
hinten.setAttribute("width", kasten.width + 8);
hinten.setAttribute("height", kasten.height + 4);
label.before(hinten);

Mauskoordinaten umrechnen

Klickt jemand in eine Grafik, liefert das Ereignis Bildschirmkoordinaten in Pixeln. Wegen viewBox und Skalierung stimmen die nicht mit den Koordinaten der Zeichnung überein. Zum Umrechnen nimmst du die Transformationsmatrix der Grafik: svg.getScreenCTM().inverse() und wendest sie auf einen DOMPoint mit den Mauskoordinaten an. Das Ergebnis ist der Punkt im Koordinatensystem der viewBox, an dem du dann zum Beispiel einen Kreis zeichnen kannst.

Zugänglich bleiben

Eine Form mit Klick-Ereignis ist für Tastaturnutzer nicht erreichbar. Klickbare Teile sollten deshalb in ein <a> gepackt werden oder tabindex="0", eine passende Rolle und eine Reaktion auf die Eingabetaste bekommen.

Wie die Grafik am Ende schlank und für alle nutzbar wird, ist Thema des letzten Kapitels: SVG optimieren und zugänglich machen.

Zum Weiterlesen