10.5Zugriff auf das DOM
Möchte man auf ein Element einer SVG-Grafik zugreifen, benötigt dieses Element in der Regel eine ID. Dadurch ist gewährleistet, daß das Element im Baum eindeutig identifiziert und somit von identischen Objekten unterschieden werden kann. Das folgende Listing 10-12 zeigt einen solchen Zugriff auf ein SVG-Dokument.
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <script type="text/ecmascript"> <![CDATA[ function toggleColor(evt) { var textlink = evt.target; var rectangle = document.getElementById("square"); if(evt.type == "mouseover") { rectangle.setAttributeNS(null, "fill", "brown"); } else { rectangle.setAttributeNS(null, "fill", "gold"); } } ]]> </script> <text x="10" y="20" style="font-size:11pt" onmouseover="toggleColor(evt)" onmouseout="toggleColor(evt)"> Berühre diesen Text, um die Farbe des Quadrats zu ändern. </text> <rect id="square" x="10" y="30" height="100" width="100" fill="gold" stroke="black" /> </svg>
Die Aufgabe der Funktion toggleColor()
besteht in der Änderung der Farbe des Quadrats in Abhängigkeit der beiden Mausereignisse mouseover
und mouseout
. Bei dem Übergabeparameter evt
handelt es sich um eine Referenz auf ein Objekt des Typs Event
- also um ein Ereignisobjekt. Dabei ist zu beachten, daß evt
beim Funktionsaufruf wie auch im Funktionskopf zwingend angegeben werden muß. Mit Hilfe von evt
bzw. dessen Eigenschaft target
erhält man eine Referenz auf das <text>
-Element, die in der Variablen textlink
gespeichert wird. Um ebenfalls eine Referenz auf das Quadrat zu erhalten, wird die Methode getElementById()
des document
-Objekts verwendet. Sie erhält als Parameter den Wert des id
-Attributs des <rect>
-Elements - in diesem Fall square
. Anschließend wird das Quadrat, sollte sich der Mauszeiger darin befinden, braun eingefärbt. Befindet sich der Zeiger außerhalb des Quadrats, erhält es die Farbe Gold.
10.5.1DOM Level 1 vs. DOM Level 2
Für die Manipulation von Elementen und Attributen eines SVG-Dokuments existieren spezielle Methoden, die zum einen dem DOM Level 1 und zum anderen dem DOM Level 2 zugeordnet werden können. Da die DOM Level 1-Spezifikation [ W3C05i] vor der Spezifikation Namespaces in XML 1.0 [ W3C05h] veröffentlicht wurde, kennt DOM Level 1 keine Namensräume, so daß die Verwendung dieser Methoden bei vielen modernen XML-Sprachen zu Schwierigkeiten führen kann. Um diese Probleme zu umgehen, wurde das Document Object Model zum DOM Level 2 Core [ W3C05j] weiterentwickelt. Tabelle 10-2 beinhaltet alle DOM Level 1-Methoden und deren DOM Level 2-Äquivalente, wobei nur noch letztere Verwendung finden sollten.
DOM Level 1 (veraltet) | DOM Level 2 |
---|---|
createAttribute() | createAttributeNS() |
createElement() | createElementNS() |
getAttributeNode() | getAttributeNodeNS() |
getAttribute() | getAttributeNS() |
getElementsByTagName() | getElementsByTagNameNS() |
getNamedItem() | getNamedItemNS() |
hasAttribute() | hasAttributeNS() |
removeAttribute() | removeAttributeNS() |
removeNamedItem() | removeNamedItemNS() |
setAttribute() | setAttributeNS() |
setAttributeNode() | setAttributeNodeNS() |
setNamedItem() | setNamedItemNS() |
Der erste Parameter aller namenraumsfähigen DOM-Methoden ist grundsätzlich der Namensraumbezeichner - meistens eine URI und deshalb auch oft Namensraum-URI genannt. Im Falle von SVG wäre dies z.B. http://www.w3.org/2000/svg
. Zu beachten ist allerdings, daß der Namensraumbezeichner für präfixlose Attribute keinen Wert besitzt, d.h. obwohl ein Attribut zum Namensraum seines Elements gehört, verwendet man nicht den Namensraumbezeichner dieses Elements. Stattdessen muß null
als Bezeichner für solche Attribute angegeben werden. Hat man also beispielsweise mit el = document.createElementNS("http://www.w3.org/2000/svg", "rect");
ein Rechteck erzeugt und möchte auf das Attribut x
dieses Rechtecks zugreifen, so muß man el.getAttributeNS(null, "x");
dazu verwenden.
Sollen Attribute manipuliert werden, die nicht zum selben XML-Dialekt gehören wie das Element, in dem sie sich befinden, sprich die mit einem Namensraumpräfix ausgestattet sind, so muß man den entsprechenden DOM Level 2-Methoden deren Namensraum übergeben. Um z.B. den Wert eines xlink:href
-Attributs eines <a>
-Elements auszulesen, würde man eine Anweisung der Form el.getAttributeNS("http://www.w3.org/1999/xlink", "href");
benutzen. Beim Setzen von Attributen ist es im übrigen ratsam, wenn auch nicht notwendig, im zweiten Argument dem Attributnamen das entsprechende Präfix gefolgt von einem Doppelpunkt voranzustellen (z.B. el.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", "image.png");
), so daß das DOM später einfacher in ein XML überführt werden kann.
Listing 10-13 faßt all das oben Genannte zu einem Beispiel zusammen und demonstriert wie man dynamisch Elemente erzeugen und bearbeiten sollte.
var svgNS = "http://www.w3.org/2000/svg"; var xlinkNS = "http://www.w3.org/1999/xlink"; var image = document.createElementNS(svgNS, "image"); image.setAttributeNS(null, "width", "100"); image.setAttributeNS(null, "height", "100"); image.setAttributeNS(xlinkNS, "xlink:href", "image.png");
Um Probleme mit diversen SVG-Betrachtern von vorherein zu vermeiden, sollte man also innerhalb eines SVG-Dokuments generell Namensräume deklarieren und Elemente nur mit den eben vorgestellten DOM Level 2-Methoden bearbeiten. Die folgende Schablone aus Listing 10-14 beherrbergt die wichtigsten Namensraumdeklarationen und kann somit dem Entwickler u.U. einiges an Ärger ersparen.
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"> </svg>
10.5.2Zugriff auf Knoten
Bevor man einen Knoten verschieben oder löschen kann, muß man selbstverständlich erst einmal Zugriff auf ihn bekommen. Bewerkstelligen läßt sich dies mit der oben bereits erwähnten Methode getElementById()
des SVG-Dokumentobjekts, welche als Parameter die ID des Elements erhält, auf das man zugreifen möchte.
Eine weitere nützliche Methode, um auf mehrere Knoten gleichzeitig zugreifen zu können, nennt sich getElementsByTagNameNS()
. Sie erwartet als ersten Parameter einen Namensraumbezeichner und als zweiten einen Elementtyp, wie etwa rect
, circle
oder path
. Als Rückgabewert bekommt man eine Liste aller Elemente desselben Typs. Um eine Liste aller Elemente der SVG-Grafik zu erhalten, wird anstelle des Typbezeichners *
angegeben.
Im Zusammenhang mit getElementsByTagNameNS()
sollte auch noch kurz das Interface NodeList
erwähnt werden. Es besitzt zum einen das Attribut length
, mit dem die Länge der Liste ermittelt werden kann, und zum anderen die Methode item()
, welche eine Referenz auf ein Node
-Objekt (einen Knoten) der Liste liefert. Dazu erhält item()
als Parameter den Index des gewünschten Objekts. Wie man length
und item()
einsetzten kann, zeigt das folgende Listing 10-15. Das Ergebnis dieses SVG-Codes findet sich in Abbildung 10-2.
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <script type="text/ecmascript"> <![CDATA[ function getColors(evt) { var svgNS = "http://www.w3.org/2000/svg"; var textlink = evt.target; var allRects = document.getElementsByTagNameNS(svgNS, "rect"); var msg = ""; for(var i = 0; i < allRects.length; i++) { var oneRect = allRects.item(i); msg += "Farbe des " + (i + 1) + ". Rechtecks: " + oneRect.getAttributeNS(null, "fill") + "\n"; } alert(msg); } ]]> </script> <text x="10" y="20" onclick="getColors(evt)" style="font-size:11pt"> Klicke auf diesen Text, um alle verwendeten Farben aufgelistet zu bekommen. </text> <rect x="10" y="30" height="30" width="30" fill="gold" /> <rect x="50" y="30" height="30" width="30" fill="orange" /> <rect x="90" y="30" height="30" width="30" fill="brown" /> <rect x="130" y="30" height="30" width="30" fill="black" /> </svg>
10.5.3Manipulation von Attributen
Nun wieder zurück zu dem Beispiel aus Listing 10-12. Der Variablen rectangle
wurde durch die Methode getElementById()
eine Referenz auf das Rechteck übergeben. Um nun die Attribute des Rechtecks ändern zu können, werden spezielle Methoden benötigt, die jedes Elementobjekt besitzt. Die beiden wichtigsten sind getAttributeNS()
und setAttributeNS()
. getAttributeNS()
erhält neben dem Namensraumbezeichner zustzlich den Namen des gewünschten Attributs als Übergabeparameter und liefert als Rückgabewert dessen Inhalt. setAttributeNS()
erhält ebenso als ersten Parameter den Namensraumbezeichner gefolgt von dem Namen des zu setzenden Attributs und dessen neuen Wert.
Stileigenschaften können natürlich auch durch die Methoden getAttributeNS()
und setAttributeNS()
ausgelesen bzw. gesetzt werden. Möchte man aber nur eine einzige Stileigenschaft ändern, so muß man dennoch die gesamte Zeichenkette, die alle Eigenschaften enthält, setzen. Dazu ein Blick auf Listing 10-16, das eine etwas veränderte Form des vorherigen Beispiels aus Listing 10-12 zeigt.
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <script type="text/ecmascript"> <![CDATA[ function toggleColor(evt) { var textlink = evt.target; var rectangle = document.getElementById("square"); if(evt.type == "mouseover") { rectangle.setAttributeNS(null, "style", "fill:brown;stroke:black"); } else { rectangle.setAttributeNS(null, "style", "fill:gold;stroke:black"); } } ]]> </script> <text x="10" y="20" style="font-size:11pt" onmouseover="toggleColor(evt)" onmouseout="toggleColor(evt)"> Berühre diesen Text, um die Farbe des Quadrats zu ändern. </text> <rect id="square" x="10" y="30" height="100" width="100" style="fill:gold;stroke:black" /> </svg>
Das Auslesen eines style
-Attributs mittels getAttributeNS()
bzw. die Verarbeitung der darin enthaltenen Daten macht die Sache noch etwas komplizierter. Denn um an die gewünschten Stileigenschaften zu gelangen, muß man die zurückgelieferte Zeichenkette komplett nach diesen durchsuchen.
Um Stilattribute zu setzen, gibt es auch noch das sogenannte CSSStyleDeclaration
-Objekt. Der Aufruf elementObj.getStyle()
liefert ein solches Objekt mit einer Liste aller Stilattribute von elementObj
. Analog zu den Methoden getAttributeNS()
und setAttributeNS()
besitzt dieses Objekt die Methoden getPropertyValue()
und setPropertyValue()
. Außerdem kann man mittels removeProperty()
ein Stilattribut aus der Liste löschen.
Obwohl es sich hierbei um eine äußerst elegante Möglichkeit handelt, Stileigenschaften zu manipulieren, ist vom Einsatz des CSSStyleDeclaration
-Objekts unbedingt abzuraten. Denn es handelt sich hierbei um eine von Adobe entwickelte Erweiterung, die nicht der SVG-Spezifikation entspricht. Um kompatiblen SVG-Code zu erzeugen, der nicht nur auf dem Adobe SVG Viewer funktioniert, sollte man also auf jeden Fall auf dieses Objekt verzichten.
10.5.4Elemente entfernen und erstellen
Ein Element kann nicht nur manipuliert, sondern auch komplett aus dem Baum entfernt oder durch ein anderes Element ersetzt werden. Hierfür gibt es die zwei Methoden removeChild()
und replaceChild()
. Als Parameter erhält die Methode removeChild()
eine Referenz auf den zu löschenden Knoten. replaceChild()
hingegen besitzt zwei Parameter, von denen der erste auch eine Referenz auf den zu löschenden Knoten und der zweite eine Referenz auf einen neuen Knoten beinhaltet. Damit diese Methoden angewendet werden können, muß das Vaterelement des zu löschenden oder zu ersetzenden Knotens bekannt sein. Die Eigenschaft parentNode
eines Knotens liefert dessen Vaterknoten.
Erstellen läßt sich ein Element mit createElementNS()
und einem Elementtyp als Parameter. Danach werden mittels setAttributeNS()
die Eigenschaften gesetzt und das neue Element durch appendChild()
dem Baum hinzugefügt. Dabei erhält appendChild()
als Parameter den neu erzeugten Knoten.
Listing 10-17 zeigt wie man parentNode
, removeChild()
, createElementNS()
und appendChild()
einsetzen kann.
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <script type="text/ecmascript"> <![CDATA[ function changeElement(evt) { var svgNS = "http://www.w3.org/2000/svg"; var textlink = evt.target; var rectangle = document.getElementById("square"); var parent = null; var circle = null; if(rectangle) { parent = rectangle.parentNode; parent.removeChild(rectangle); circle = document.createElementNS(svgNS, "circle"); circle.setAttributeNS(null, "cx", 60); circle.setAttributeNS(null, "cy", 80); circle.setAttributeNS(null, "r", 50); circle.setAttributeNS(null, "fill", "gold"); circle.setAttributeNS(null, "stroke", "black"); parent.appendChild(circle); } else { alert("Das Quadrat wurde schon durch den Kreis ersetzt!") } } ]]> </script> <text x="10" y="20" onclick="changeElement(evt)" style="font-size:11pt"> Um das Quadrat durch einen Kreis zu ersetzen, klicke auf diesen Text. </text> <rect id="square" x="10" y="30" height="100" width="100" fill="gold" stroke="black" /> </svg>
10.5.5Zugriff auf einen Text
Damit der Inhalt des <text>
-Elements ausgelesen und verändert werden kann, benötigt man zuerst eine Referenz auf dessen Knoten. Mit der Eigenschaft firstChild
erhält man anschließend den Zugriff auf den ersten Subknoten. Dieses Text-Objekt besitzt u.a. die folgenden Attribute und Methoden.
Attribute:
length
- beinhaltet die Anzahl der Zeichendata
- enthält die Zeichenkette
Methoden:
insertData()
- eine neue Zeichenkette wird in die bereits bestehende eingefügtappendData()
- eine neue Zeichenkette wird an die bereits bestehende angehängtreplaceData()
- ersetzt einen Teil der ZeichenkettesubstringData()
- liefert einen Teil der Zeichenkette zurückdeleteData()
- entfernt einen Teil der Zeichenkette
Der SVG-Code in Listing 10-18 benutzt die eben vorgestellten Eigenschaften und Methoden und demonstriert deren Arbeitsweise.
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <script type="text/ecmascript"> <![CDATA[ var i = 0; function changeText(evt) { var textElement = evt.target; text = textElement.firstChild; switch(i) { case 0: alert("Aktueller Inhalt des Textknotens: " + text.data); i++; break; case 1: text.insertData(text.length, " Fragt der Barkeeper:"); alert("Neuer Inhalt: " + text.data); i++; break; case 2: text.appendData(" \"Wieso so'n langes Gesicht?\""); alert("Die Zeichenkette '\" Wieso so'n langes Gesicht?\"' wurde an den Text angehängt."); i++; break; case 3: text.replaceData(49, 5, "Weshalb"); alert("Die Zeichenfolge 'Wieso' wurde durch 'Weshalb' ersetzt."); i++; break; case 4: alert("Zwischen den Zeichen 9 und 15 befindet sich folgendes Tier: " + text.substringData(9, 5)); i++; break; case 5: text.deleteData(0, text.length); alert("Der gesamte Text wurde entfernt."); i++; break; } } ]]> </script> <text x="10" y="20" style="font-size:11pt;font-weight:bold">Klicke bitte auf den untenstehenden Text.</text> <text x="10" y="40" onclick="changeText(evt)" style="font-size:11pt">Kommt 'n Pferd in 'ne Bar.</text> </svg>