Scripting SVG

Creating truly dynamic SVG

Christian Wenz

Libauer Str. 2
D-81927 Munich, Germany
e-mail: chw@hauser-wenz.de

Tobias Hauser

Prinz-Karl-Str. 30b
D-82319 Starnberg, Germany
e-mail: th@hauser-wenz.de

webpage: http://www.hauser-wenz.de/

Keywords: scripting; ECMAScript; JavaScript

Abstract

SVG itself is very powerful, and a lot of reasons for that will be presented at the SVG Open / Carto.net Developers Conference. However in order to compete with Flash, developers need tools to match with ActionScript. ActionScript extends the functionality of Flash with scripting capabilities, using an embedded scripting engine. JavaScript - or its standardization ECMAScript - is a tool that is very similar to ActionScript (and even the language the designers of ActionScript had in mind when creating their language), and JavaScript support is a vital ingredient to a truly dynamic SVG animation. Quite a lot of web designers know JavaScript fairly well as it is the most widely used client-side scripting language.

With its <script> element, SVG is ready for includion of ECMAScript code. The currently most widely used SVG viewer, Adobe SVG Viewer, supports ECMAScript and even gives the programmer the coice between using the browser's script engine and the viewer's built-in script engine.

It is impossible to cover the scripting capabilities of SVG in 30 minutes, so you will get an introduction to the most important concepts; further information will be available in the workshop about this topic.

Introduction

SVG vs. Flash - that's where some people trade their intelligence for ideology. No, SVG will not save the world. Flash won't, either. A very strong point with flash is ActionScript, the built-in scripting language that enables very sophisticated scripts. In this talk, we will have a look at the counterpart of SVG, the support for ECMAScript code. If you know JavaScript (or ActionScript), you do know ECMAScript. We will not show you how the language works but how to use it with and within Flash.

SVG itself is fully DOM Level 2 compliant, so you have full access to the methods and functionality provided in the standard, e.g. getElementById(). We will jump over these concepts and dive right into coding.

Embedding scripts

In order to embed script code within an SVG document, you can use the <script> element that will be placed within an <defs> element:

<?xml version="1.0" ?>
<svg width="..." height="...">
  <defs>
    <script type="text/ecmascript"><![CDATA[
      // ...
    ]]></script>
  </defs>
  <!-- ... -->
</svg>

Since you do want to manipulate the SVG data with your script code, you will always need a reference to the SVG document. This is a little bit tricky, but once it's done, you can re-use this code in all applications. First of all, you have to instruct the script interpreter to execute a function when the SVG document has been fully loaded:

<svg onload="init(evt);">

The function init() will now be executed when the SVG document has been completely transfered to the viewer (e.g. the browser). In this function, the parameter to the call - the current event, "loading completed" - is submitted. Using this parameter, a reference to the SVG document is retrieved:

var svgdoc;  // define globally!

function init(evt) {
  svgdoc = evt.getTarget().getOwnerDocument();
}

Here is a complete listing thas sets variable svgdoc and additionally creates an alert box that shows the string representations of evt and svgdoc:

<?xml version="1.0" ?>
<svg onload="init(evt);" width="150px" height="100px">
  <defs>
    <script type="text/ecmascript"><![CDATA[
      var svgdoc;

      function init(evt) {
        svgdoc = evt.getTarget().getOwnerDocument();
        alert("evt: " + evt + 
              "\nsvgdoc: " + svgdoc);
      }
    ]]></script>
  </defs>
</svg>

As you can see, the string representation of these two variables shows that objects are involved:

The string representation of the two variables

Figure 1: The string representation of the two variables

And indeed - these are objects. When you have a look at the SVG specs, there exist a variety of so-called SVG interfaces. Each of this interface represents a certain SVG structure, an object or just an element and has a set of methods and functions you can access via ECMAScript (and other scripting languages, as well). So keep your reference handy, as it will be your guide while developing.

Modifying the DOM tree

As we have mentioned earlier, the SVG is DOM Level 2 compliant. Thus, the contents of an SVG file can be represented in a tree structure. ALso, the usual functions and methods can be used to modify this tree. Here are basically two ways to access elements within an SVG document:

The most common way, however, is using getElementById(). Moving up and down the SVG tree is quite often of rather academical value (direct access is much less code); very, very few of the existing SVG applications use that extensively.

Changing elements

In the next example, we will display the current date using ECMAScript. For this, we create a <text> element and access this element using ECMAScript. We use both techniques presented above:

<?xml version="1.0" ?>
<svg onload="init(evt);" width="150px" height="100px">
  <defs>
    <script type="text/ecmascript"><![CDATA[
      var svgdoc;

      function init(evt) {
        svgdoc = evt.getTarget().getOwnerDocument();
        var dt = svgdoc.getElementById("datetext");
        var d = (new Date()).toUTCString();
        dt.firstChild.data = d;
      }
    ]]></script>
  </defs>
  <text id="datetext" x="10" y="50"> </text>
</svg>

As you can see, the current system date is displayed in the SVG file:

The current date is displayed

Figure 2: The current date is displayed

Changing attributes

Apart from changing the whole content of an element, it is also possible to read and write single attributs. In order to do so, you can use the following two functions:

As you might have seen in the previous example, the current date may not be fully displayed since the width of the SVG document is to small. Thus, we want to create an animation that moves the date from left to right and back. In order to do so, we read out the x position of the text and increase or decrease it on a permanent level. The most important code line is the following:

dt.setAttribute("x", parseInt(dt.getAttribute("x")) + 5 * factor);

The current x position is read, increased or decreased (factor is either +1 or -1) and then the new x position is set. Here is the complete code:

<?xml version="1.0" ?>
<svg onload="init(evt);" width="150px" height="100px">
  <defs>
    <script type="text/ecmascript"><![CDATA[
      var svgdoc

      var factor = -1;

      function animate() {
        var dt = svgdoc.getElementById("datetext");
        var d = (new Date()).toUTCString();
        dt.firstChild.data = d;
        dt.setAttribute("x", parseInt(dt.getAttribute("x")) + 5 * factor);
        if (parseInt(dt.getAttribute("x")) >= 10 || 
            parseInt(dt.getAttribute("x")) < -100 )
          factor *= -1;
      }

      function init(evt) {
        svgdoc = evt.getTarget().getOwnerDocument();
        window.setInterval("animate();", 200);
      }
    ]]></script>
  </defs>
  <text id="datetext" x="10" y="50"> </text>
</svg>

The current date is displayed and animated

Figure 3: The current date is displayed and animated

Keyboard events

Capturing keyboard events did not make it into the final version of DOM Level 2. It was there in a preliminary version, but was removed in the process. DOM Level 3 will most probably include keyboard events, but with the current version, SVG does not "officially" support keyboard events.

However there is one exception. The most widely-used SVG viewer, Adobe SVG Viewer 3.0, does suppport keyboard events. This does not conform to the specification, however if you konow that your target audience uses the Adobe viewer, you can use these techniques.

The only thing you have to do is to add an event listener to a special element for one of the keyboard events, e.g. keypress (is fired when a key is pressed):

element.addEventListener("keypress", function_name, false);

Now it is possible to access the pressed key. The following example displays the text the user types in:

<?xml version="1.0" ?>
<svg onload="init(evt);" width="150px" height="100px">
  <defs>
    <script type="text/ecmascript"><![CDATA[
      var svgdoc, kt;

      function handlekeypress(evt) {
        var key = String.fromCharCode(evt.getCharCode());
        kt.firstChild.data += key;
      }

      function init(evt) {
        svgdoc = evt.getTarget().getOwnerDocument();
        kt = svgdoc.getElementById("keytext");
        kt.addEventListener("keypress", handlekeypress, false);
      }
    ]]></script>
  </defs>
  <text id="keytext" x="10" y="50">Type here: </text>
</svg>

Keyboard events are captured

Figure 4: Keyboard events are captured

Note that you have to click on the text in order to be able to type something.

From HTML to SVG

Apart from putting ECMAScript code in the SVG file itself, it is also possible to access the SVG file (and all properties and values) from JavaScript code that is embedded in HTML code.

One problem with this approach is Netscape 4.x which has still far more users than Netscape 6, 7 and Mozilla combined; so this browser still has to be supported in professional web development. Unfortunately, <object> leads to trouble. On some systems, embedded SVG data within <object> does not work at all. On those systems that have no problems, accessing the SVG data from HTML does not work. The only way to go in this situation: <embed> must be used, although it is deprecated now.

If you want to access an embedded SVG document using JavaScript, the name attribute of the <embed> tag (or the <object> tag, if you want to ignore Netscape 4.xd users) must be accessed. The method getSVGDocument() returns a reference to the SVG document, as does the function init() we wrote for the previous examples:

var svgdoc = document.SVG.getSVGDocument();

You now have access to all SVG object as if you would script within the SVG document. In order to demonstrate this, we use a simplified version of a previous example - the clock:

<?xml version="1.0" ?>
<svg width="150px" height="100px">
  <text id="datetext" x="10" y="50"> </text>
</svg>

The following HTML code does the following:

Here is the complete (HTML) code:

<html>
<head>
<title>From HTML to SVG</title>
<script type="text/javascript"><!--
var svgdoc;
window.onload = init;

function init() {
  svgdoc = document.SVG.getSVGDocument();
}

function update() {
  var dt = svgdoc.getElementById("datetext");
  var d = (new Date()).toUTCString();
  dt.firstChild.data = d;
}
//--></script>
</head>
<body>
<embed name="SVG" type="image/svg+xml" src="date_blank.svg" />
<form>
  <input type="button" value="Update" onclick="update();" />
</form>
</body>
</html>

If your browser supports JavaScript, you can try the code right away:

Figure 5: Upon mouse click the SVG displays the current date and time

Summary

In this talk we showed you some effects you can achieve by combining the powers of ECMAScript and SVG. Among these were changing elements of an SVG page, animation and keyboard interaction. It is obvious that these are only very few of the possibilities that the "dynamic duo" SVG & ECMAScript offers. Or, to borrow a little bit from a well-known TV series, the only bounding frontier is your own imagination ...

References

[1] "SVG Unleashed", A. Watt, C. Wenz, T. Hauser, D. Ayers, K. Lindsey, R. George, C. Lilley. Sams Publishing, to be published in Sepmtember 2002.


Valid XHTML 1.1!