SVG und JavaScript/DOM-Scripting

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

SVG verwendet genau wie HTML das DOM (Document Object Model), bei dem Sie mit JavaScript auf jedes SVG-Element einfach zugreifen können.

Vor diesem Kapitel sollten die Themen der Kurse Einstieg in SVG und Einstieg in das DOM bekannt sein, da die dort besprochenen Methoden hier eingesetzt werden.

Inline-SVG

Wenn Sie SVG-Fragmente inline in HTML-Webseiten integrieren, sind die SVG-Elemente Teil des DOM. Sie können auf jedes SVG-Element wie auf ein beliebiges HTML-Element zugreifen.

Event-Handling

JavaScript wird im Allgemeinen beim Laden der Seite geladen und dann durch Benutzeraktionen aufgerufen. Diese Aktionen nennt man auch Events (dt. Ereignisse) .[1]


Werte mit setAttribute() ändern ansehen …
document.addEventListener('DOMContentLoaded', function () {
	function changeFill () {
		var farben = new Array(
			'red', 'skyblue', '#dfac20', '#ebf5d7','#e4ebf2', '#5a9900',
			'lime', '#df6c20', 'brown', '#5c82d9', 'burlywood',
			'blueviolet', '#c32e04', '#ffebe6' ,'#5c82d9'
		);

		var fuellFarbe = Math.floor(farben.length * Math.random());

		if (fuellFarbe == farben.length) {
			fuellFarbe = farben.length-1;
		}

		fuellFarbe = farben[fuellFarbe];
		kreis.setAttribute('fill', fuellFarbe);
	}

	document.getElementById('kreis').addEventListener('click', changeFill);
	document.getElementById('text').addEventListener('click', changeFill);
});

Der Kreis wird mit document.getElementById angesprochen und erhält mit addEventListener einen Event-Handler zugewiesen. Der Text ebenfalls, sonst passiert ausgerechnet dann nichts, wenn der Benutzer tatsächlich auf den Text (und nicht auf die Kreisfläche daneben) klickt.
Wenn nun auf den Kreis geklickt wird, wird die Funktion changeFill() aufgerufen, die nach dem Zufallsprinzip Farbwerte aus einem Array ausliest und dem Kreis mit setAttribute() zuweist.

SVG-Elemente erzeugen und löschen

Im Vergleich zu HTML wirkt die Syntax von SVG mit den vielen Geometrie-Attributen unübersichtlich. Anstatt Elemente direkt im Markup zu notieren, ist es oft vorteilhaft, diese mit aus aktuellen Berechnungen oder Eingaben ermittelten Werten dynamisch zu erzeugen.

Ellipsen erzeugen und löschen - das SVG-Markup ansehen …
<svg viewbox="0 0 880 450">

	<text x="60" y="420">Erzeuge Ellipse!</text>
	<rect id="createButton" tabindex="1" x="50" y="400" width="150" height="30" rx="5"  />

</svg>
Für die Steuerung legen wir einen Button als rect-Element an. Im Unterschied zum oberen Beispiel legen wir den Text darunter, damit wir keine doppelten EventListener benötigen, und geben dem Button eine fill-opacity: .5, damit der Text durchscheint.
Beachten Sie: Unser „Button“ ist noch kein Button, da ihm die Funktionalität fehlt. Diese wird mit tabindex="1" als XML-Attribut und cursor: pointer im CSS erreicht.
Ellipsen erzeugen - createEllipe() ansehen …
	function createEllipse(){
		svg.appendChild( getNode('ellipse', { 
			id: 'ellipse' + count,
			cx: rand(0,880), 
			cy: rand(0,400), 
			rx: 30, 
			ry: 20, 
			fill: colors[rand(0, colors.length-1)]
		}) );
		count++;
	}	
	
	function getNode(elem, v) {
		elem = document.createElementNS("http://www.w3.org/2000/svg", elem);
		for (var p in v)
			elem.setAttribute(p, v[p]);
		return elem;
	}

Die Helferfunktion getNode erzeugt aus den zwei Parametern elem und v ein neues SVG-Element. Anstelle des evtl. schon bekannten Document.createElement(), die ein HTML-Element erzeugt, wird hier die XML-Variante createElementNS() verwendet.

Die in v übergebenen Attribute werden mit ihren Werten mit elem.setAttributeNS() zugewiesen.

Ellipsen erzeugen und löschen ansehen …
function removeEllipse(event) {
	var elem = event.target; 
	if ('rect' != elem.nodeName) {
		var parent    = elem.parentNode;
		parent.removeChild(elem);
	}
}

document.querySelector('svg').addEventListener('click', removeEllipse);

Ein weiterer EventListener lauscht auf Klicks in das svg-Element. Falls das angeklickte Elemente kein rect-Element (der Button) ist, wird das Elternelement (das svg-Element) aufgerufen und dessen Kindelement mit removeChild gelöscht.

Empfehlung: Natürlich ist es möglich, svg-Elemente als Buttons, Menüs und Navigationen verwenden und sie durch tabindex und cursor entsprechend zugänglich zu machen. Einfacher ist es, …
  • SVG für die Beschreibung grafischer Inhalte zu verwenden
  • für Interaktionen die passenden HTML-Elemente mit ihrem Standardverhalten zu verwenden.

Rahmen um Text erzeugen

SVG-Text besteht nur aus den Zeichen oder Glyphen. Ein „Hintergrund“, den man wie bei HTML-Elementen mit CSS gestalten könnte, existiert nicht, kann aber mit JavaScript dynamisch erzeugt werden.

SVG-Markup ansehen …
<svg xmlns="http://www.w3.org/2000/svg">

  <text id="txt" x="20" y="40" font-size="24" >Das ist der Text.</text>

</svg>

Mit Javascript können wir den Text nun selektieren und seine Dimensionen auslesen.

Rahmen erzeugen ansehen …
document.querySelector('button').addEventListener('click', drawBanner);	

function drawBanner(){ 
  const horizontalPadding = 1.05,
        lineHeight = 1.25;  //größer wegen der Unterlängen!
  var textobj = document.getElementById('txt'),
      textWidth = textobj.getComputedTextLength().toFixed(1),
      textHeight = textobj.getAttribute("font-size"),
      rectobj = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
  rectobj.setAttribute('x', textobj.getAttribute('x') - 5);
  rectobj.setAttribute('y', textobj.getAttribute('y') - textHeight);
  rectobj.setAttribute('height', (lineHeight * textHeight).toFixed(1));
  rectobj.setAttribute('width', (horizontalPadding * textWidth).toFixed(1));
  textobj.parentNode.insertBefore(rectobj, textobj);
};

Mit getComputedLength() wird die Länge des vorhandenen Texts ermittelt und der Variable textWidth übergeben. Die Höhe wird aus der Schriftgröße, die im font-size-Attribut festgelegt wurde, ermittelt und textHeight zugewiesen. (Wenn die Schriftgröße nur im CSS festgelegt wäre, müsste sie dort mit getPropertyCSSValue()ermittelt werden.)

Jetzt wird mit createElementNS() ein neues SVG-Element erzeugt und ihm mit setAttribute() die Geometrie-Attribute mit den passenden Werten hinzugefügt. Die Breite und Höhe errechnet sich aus den ermittelten Werten des Textes, die mit horizontalPadding und lineHeight, die als Konstanten definiert werden, multipliziert werden. Da in der font-size Unterlängen nicht berücksichtigt wurden, ist der Wert für lineHeight höher.

Anschließend wird das neue rect-Element mit insertBefore vor dem Text gezeichnet, da es ja (noch) keinen z-index bei SVG gibt. So wird erst der Hintergrund und Rahmen und dann davor der Text gezeichnet.

externe SVG-Dokumente ansprechen

Externes SVG-Dokument: Beispiel.svg

Eine solche Veränderung des DOMs einer SVG-Grafik ist auch bei externen SVGs möglich. Im folgenden Beispiel wird die rechts gezeigte Grafik in die Webseite eingebunden und dann mit JavaScript verändert:

mit object eingebundene SVG ansehen …
<object id="svgObject" data="Beispiel.svg" type="image/svg+xml" height="250" width="600">
  SVG-Grafik konnte nicht geladen werden!
</object>

Achtung!

Stellen Sie sicher, dass sich HTML-Datei und SVG auf dem gleichen Server befinden!

Um Cross Site Scripting (XSS) zu verhindern, unterbinden moderne Browser das Laden externer SVG-Grafiken von anderen Domains, da diese ja auch JavaScript enthalten können.
das Script ansehen …
document.addEventListener('DOMContentLoaded', function () {

	// Get the Object by ID
 	var mySVG = document.getElementById('svgObject'),
	    svgDoc;
 	// Get the SVG document inside the Object tag - Wait until it's loaded!
 	mySVG.addEventListener('load',function() {
		svgDoc = mySVG.contentDocument;
		alert('SVG contentDocument is geladen!'); 
 	}, false);	
					
	 function changeFill () {
			var svgItem = svgDoc.getElementById('quadrat');
			svgItem.setAttribute('fill', '#5a9900');	  
	}
	
	document.querySelector('#button').addEventListener('click', changeFill);

});

Wenn das im object-Element enthaltene (SVG-)Dokument und die Webseite von gleicher Herkunft sind (Same-Origin-Policy), kann man mit contentDocument auf den Inhalt des externen SVG-Dokuments zugreifen.

Die einfache Zuweisung svgDoc = mySVG.contentDocument; hat bei mir nicht funktioniert, bzw. null ergeben, da das externe Objekt beim Ausführen des Scripts noch nicht geladen war. Die anonyme Funktion, die erst nach dem Laden des Objekts feuert, stellt sicher, dass die SVG-Grafik auch wirklich zur Verfügung steht.

Anwendungsbeispiele

Farben einer SVG-Karte aktualisieren

Stellen Sie sich eine Webseite vor, auf der Sie eine interaktive Karte mit Ländern anzeigen. Jedes Land auf der Karte wird als Pfad in einer externen SVG-Datei dargestellt. Benutzer können auf ein Land klicken, um es zu markieren, oder dynamisch Daten laden (z. B. Bevölkerung, BIP), um die Karte farblich zu gestalten.

ToDo (weitere ToDos)

muss erstellt werden.

--Matthias Scharwies (Diskussion) 06:10, 20. Nov. 2024 (CET)

Weblinks

  1. W3C: Chapter 15: Scripting and Interactivity
  2. W3C: setAttributeNS