Chapter 10 : Scripting the DOM

trampknowledgeSoftware and s/w Development

Nov 4, 2013 (3 years and 9 months ago)

211 views

Chapter 10 : Scripting the DOM
What we’ll look at in this chapter

ƒ An overview of the ECMAScript Language and the components it give us to work with

ƒ The workings of the Document Object Model

ƒ Adding and removing elements in our SVGs

ƒ Adding interactivity

ƒ Handling mouse events

ƒ Some cool examples combining these skills


The ECMAScript Language

ECMAScript is an object-oriented programming language for performing computations and
manipulating computational objects within a host environment.
To change a static non-interactive document into a highly dynamic interactive document the
W3C provides us with a couple of efficient objects embedded in a framework called the
Document Object Model
or
DOM
. Since we want to communicate with these objects, we’ll
need an appropriate language — a programming language. In this chapter we will be introduced
to a suitable programming language —
ECMA script
— and we will be introduced to the
concepts of the DOM.

Now don’t be put off by the word programming. “I am a web designer, I am no programmer” is
often the reaction. However, as a designer, I can also guarantee that you won’t want to miss out
on the power and aesthetics scripting can bring to your design. After reading this chapter you
will be able to use third party script libraries with ease. And if you are more engaged you will
have a good starting point for your own scripting projects.

So what is this ECMAthing, we need to learn?
ECMAs JavaScript Roots
A while back, Netscape decided to add a scripting language to their Navigator browser. With
that, they reasoned, Web designers would be allowed to add rich interactive and dynamic
features to their Web pages without the need to do complicated server-side CGI programming or
Java applet development. That scripting language should have the look and feel of C++ and Java.
On the other hand it had to be simple and easy to learn, so that non-programming web designers
or scripters working with Basic were not intimidated. Since everybody was talking about Java at
the time, they called it
Javascript
, although there were not many similarities between these
languages apart from the syntax.

Learn SVG Chapter 10 Scripting the DOM
2

Javascript
was a success and web sites with embedded scripts proliferated. Stimulated by this
other scripting languages emerged and threatened to litter the web with a variety of proprietary
solutions. So Netscape, Microsoft, and other major players agreed to form a standards
committee. They handed the language specification over to the
European Computer
Manufacturers’Association
’ (
ECMA
) as an Open Standard.

Today the third edition of the standard called
ECMAScript
is considered stable and powerful.
There is rarely a browser developer, that does not claim to own an ECMAScript conformant
scripting language. Netscape’s Javascript and Microsoft’s Jscript language are examples of
ECMAScript compliant languages within their respective browsers.

A Note on Scripting
A scripting language is somewhat different from a conventional programming language such as
FORTRAN, PASCAL, or C++.

A script is not translated by a compiler as a whole to form an executable program. It is usually
interpreted by a scripting engine (interpreter) statement-by-statement. ECMAScript is an
interpreted, object-based scripting language. Although it has fewer capabilities than full-fledged
object-oriented languages like C++, ECMAScript is more than sufficiently powerful for its
intended purposes as a Web Scripting Language.

The ECMA basics
This chapter won’t cover the whole of the ECMAScript language. Instead we’ll restrict the
overview to those language essentials we intend to put good use to here. An ECMAScript
program is written in
text format
. This text is organized into statements, comments and blocks.
Each block can repeatedly consist of other
statements
,
comments
and
blocks
. Within each
statement we find
data

types
,
variables
and
expressions
. Thus if we examine an admittedly
human, weekly thought process, the result in ECMAScript may appear as below.


var action = ””, today = DayOfWeek();
if (today == ”Sunday”)
action = ”sleeping”;
else if (today == ”Saturday”)
action = ”shopping”;
else // waiting for weekend ..
action = ”working”;

In the example above we come across
statements
(the
if (…) else (…)
blocks
),
comments

(waiting for weekend),
expressions
(today==”Sunday”) and
variables (action). Data Types are
also to be found, though we only use one type above.
Data Types
ECMAScript is a
loosly typed
language as opposed to the
strongly
typed conventional
programming languages. Loosly typed here means, that we – the script programmers – need not
to care explicitely about the types we use in our program. This doesn’t mean, that ECMAScript
has no different data types. ECMAScript will rather do the data type management for us and
always implicitly assign the correct data type to our variable or return the correct data type from
a function. With this we can define variables in a very convenient manner via the var statement.
Let’s have a closer look at the data types in detail ECMAScript can deal with.


Learn SVG Chapter 10 Scripting the DOM
3

Number
Number is the data type for numerical values. There is no distinction between whole numbers
(integers) and rational floating point numbers (floats) as in other languages. Here are some
literals.

-273, 1024, 0 // negative and positive integers ..
-6.28, 0.25, 66.666667 // negative and positive fractional numbers ..
0.001, .001, 1.0e-3, 1E-3 // variations of one per thousand

String
String is the type for textual data. ECMAScript does not differentiate between a multi-character
text and a single character as other languages do (for example C++ and Java). String literals are
enclosed in single
(’)
or double
(”)
quotes.

Although it is possible to use either single or double quotes to enclose these literals you should
remember that you must use them consistently. For this reason I suggest that you pick a style and
stick to it to avoid receiving an error message, simply because you’ve opened with a single quote
and closed with a double.
”a short text”, ’another text’ // the text delimiters ..
”mike’s bike”, ’he said: ”hello”’ // inverse delimiter usage ..
’c’, ’C’, ”4”, ’”’, ” ” // different single characters ..
””, ’’ // the empty string ..
”\””, ”\n”, ’\’’, ’\\’ // special characters, escape via backslash

Boolean
There is also a logical data type. This Boolean type can have one of two values.

true, false // the two Boolean values ..

In ECMAScript the values ‘
true’ and ‘false’
conform to ‘
1’ and ‘0’.
Object
Object is the most important data type — the one, that makes ECMAScript an
object oriented
language
. There are no discrete values assigned to this data type. An Object can instead be
considered as a container that embeds other data types and other Objects. We’ll cover Objects in
more detail later.
Comments
I imagine most of you are already familiar with the comment — an esteemed and trusty friend of
any coder. A comment in an ECMAScript program does nothing; it is not even executed, though
it is not useless. A comment usually concludes additional information about the program’s
source code. ECMAScript adopted the
single-line
and
multi-line
comments from C++ and Java.
A single-line comment starts with a pair of forward slashes (//) as in

else // waiting for weekend ..

and ends with the end of the line.

A multi-line comment begins with a forward slash and asterisk (/*) and ends with the reverse
pattern, an asterisk and forward slash (*/).

/* for this nontrivial
comment several
lines are required */



Learn SVG Chapter 10 Scripting the DOM
4

All text between /* .. */ is considered by ECMAScript to be non-program code. Please also note,
that multi-line comments cannot overlap or nest. I recommend you use single-line comments for
commenting your source code only. Multi-line comments can then be useful to comment
multiple lines out for debugging purposes. This means simply that if you receive an error
message, you can test different parts of your code for bugs by fencing them off with comment
slashes and running the code without them.
Variables
As in many programming languages you will use variables in ECMAScript programs
extensively. Think of a variable as a named container that can hold exactly one value. With that
we can access this value by simply using the variable’s name. Since ECMAScript is a
loosely
typed
language, a variable can hold a value of any data type. During the variable’s lifetime that
data type can even change.

var pi = 3.14, radius = 100; // define two variables ..
var circumference = 0, area = 0; // .. and another two ..
circumference = 2*pi*radius; // assign a value ..
area = pi*radius*radius; // .. to each variable
pi = ’hello’; // change pi’s data type ..

A variable gets defined by using the reserved word var, and, after this definition, can be used in
an expression. Under certain circumstances ECMAScript allows you to use a variable without
explicit definition, but it is a good programming practice and I strongly recommend you to
always define a variable.

There is very little limit on what we actually call our variables. In particular the length of the
name is not limited – at least not practically.

It is always a good practice, to choose expressive, meaningful variable names that will tell anyone,
who reads our program, what that variable is represents
We are not absolutely free with the naming of variables, we do have to follow certain rules:

• The first character has to be a lowercase or uppercase letter or an underscore.
• All subsequent characters can be letters, decimal digits and underscores.
• ECMAScript’s reserved words (var, for, if, else, ..) are not allowed as variable names.

When you are defining a variable (by var) you need not assign an initial value to it, but here too I
strongly recommend that you do so. . With this you avoid usually hard to track program bugs
resulting from computations with un-initialised variables. If you do not assign a value explicitly
while initialisation, the variable gets the value undefined by default.
Expressions
An expression is a piece of ECMAScript code that results in a certain value when it is evaluated,
as shown below.

var pi = 3.14, radius = 100, str = ”hello”; // defining some ..
var color = ”green”; // .. variables
2+3/2 // 3.5
(2+3)/2 // 2.5
radius*2/10 // 20
2*pi*radius; // 628
radius == 100 // true
str == ”blue” // false
str + ” Mary” // ”hello Mary”


Learn SVG Chapter 10 Scripting the DOM
5

str.length // 5
Math.sin(Math.PI/2) // 1
color == ”red” || color == ”blue” // false

An expression can consist of
• Numbers, Strings, Boolean literals

Operators
(+, -, *, /, …)
• Variables
• Function calls
• Object properties or object method calls

An expression’s value can take the form of a number, a string or a boolean expression.
Operator please
ECMAScript supplies us with a large set of
operators
. Most of them are binary operators (eg
‘+’) with two operands as in 2+3. There are also unary operators as in i++ with one operand i
and the increment operator ++. The operators can be subdivided into different categories.

Category

Operator

Means

Example

Equals

+

Addition

1 + 2

3

-

Subtraction

8 - 6

2

*

Multiplication

2*3

6

/

Division

10/3

3.3333333333

%

Modulus

10%3

1

++

Increment

x = 3; x++

x=4

--

Decrement

x = 5; x--

x=4

Arithmetic operators

-

Negation

-2

negative 2

+=

Addition

x += y

x=x+y

-=

Subtraction

x -= y

x=x-y

*=

Multiplication

x *= y

x=x*y

/=

Division

x /= y

x=x/y

Assignment
operators

%

Modulus

x %= y

x=x%y






+

Concatenation

"hello "+"world"

"hello world"

String operators

+=

Concatenation

a += b

a=a+b

==

is equal to

2 == 3

false

!=

does not equal

2 != 3

true

>

is greater than

5 > 3

true

>=

is greater than or
equal to

5 >= 3

true

<

is less than

5 < 3

false

Comparison
operators

<=

is less than or
equal to

5 <= 3

false

&&

AND

(3 <= 5) && (5 < 8)

true

||

OR

(5 > 3) || (3 > 5)

true

Logical operators

!

NOT

5 != 3

true


The ECMAScript ‘+’ operator has the remarkable characteristic, not only to add numbers as in
3+7, but also to concatenate strings, so “SVG “+”is “+”cool” would become “SVG is cool”.


Learn SVG Chapter 10 Scripting the DOM
6

Statements
A statement can be considered a meaningful sentence in the ECMAScript language. It typically
makes intensive use of expressions. When the statement is executed, it performs a certain action
so we can look at a program as a collection of statements in order to carry out a complete task.

var name = ”Robin”;
document.write(”Hello ” + name + ” how are you?”);

The scripting engine looks for any semicolon (;) as the terminating character of statements. With
this you can write statements that span several lines, or you can write multiple statements in a
single line. It is generally considered good programming practice to write one statement per line,
though today script engines are somewhat forgiving with missing semicolons.

Despite this I strongly recommend to always add a semicolon at the end of any statement. This will
ensure that your statements are executed exactly as you intend and will also cover you for forwards
compatibility.
An ECMAScript program is executed top down. This default program flow starts with the first
statement and ends with the last. With this sequential execution we can only write relatively
simple programs, so ECMAScript — as other programming languages — supports junctions and
repetitions in its program flow.
Conditional Statements
The conditional if statement allows us to execute a number of statements depending on the result
of a certain condition.

if (<condition>)
{
<statements>;
}

The statements following the if(..)(known as the if block) are executed only if the condition — a
Boolean expression — enclosed in the parenthesis evaluates to true. When we need to have
several statements belonging to the if, we have to enclose those within curly braces {}. In the
case of just one statement we are allowed to omit them.
Handing several possibilities with
if…else

The if..else statement adds the ability to specify alternate lines of code for a case when the if-
condition evaluates to false. For example, in the code below, the containment of a point (x, y) in
a square at (100, 200) with a size of 50 is checked.

if (x < 100 || y < 200 || x > 150 || y > 250)
inside = false;
else
inside = true;

Frequently if..else statements are concatenated, so that several conditions can be tested.

if (background == ”black”)
color = ”white”;
else if (background == ”white”)
color = ”black”;
else
color = ”blue”;


Learn SVG Chapter 10 Scripting the DOM
7

Iteration Statements
The repetitive while-statement works in a similar fashion to the if statement, with the difference
that the affiliated statements aren’t executed only once, but again and again as long as the
condition — the Boolean expression — evaluates to true.

while (hour >= 8 && hour <= 17)
{
action = ”working”;
}

The above example relies on a changing value of the variable hour. Otherwise we’d end up
implementing an endless loop, which is unable to break out of its own cycle.

Frequently there is a need to visit all elements of a container. For this we generally introduce a
counter variable, which must then be incremented with every iteration step as long as there is
another element in the container. This is commonly referred to as
initialization, condition,
update
.

var sum = 0;
for (var i=1; i<=100; i++)
{
sum += i; // same as .. sum = sum + i;
}

Will sum up all numbers from 1 to 100 resulting in a final sum value of 5050.

With this example I’ll introduce you to ECMAScript’s important for statement. Let’s have a
closer look at its syntax.

for (<initialization>; <condition>; <update>)
{
<statements>;
}

Beside the <condition>, a Boolean expression which we encountered in the while loop, we can
also have an <initialization> and <update> section here in the for-loop. This works according to
the following instructions:

• Enter the
for
loop.
• Define and/or initialize one or more variables in the
<initializing>
section.

Evaluate the
<condition>.
• If the
<condition>
evaluates to
true
, enter the loop’s body. Otherwise leave the loop
and execute next code

Otherwise — Execute the
<statements>.
• The
<incrementing>
section consists of addition to, or subtraction from, the value.
Return to condition.

The for loop is the most frequently used loop in C++, Java and ECMAScript, because it comes
with the ability to also define, initialise and update the iteration variable. Please note, that we can
leave one, two or even all of the three sections <initialization>; <condition>; <update> blank.
But you must not omit any of the semicolons.

Adherent to the iteration statement is the break statement. Whenever it is executed, the
immediate surrounding loop will be exited.


Learn SVG Chapter 10 Scripting the DOM
8


for (;;) // ’forever’ .. all sections are empty ..
{
if (hour >= 22)
break;
action = ”working”;
}

There are more conditional and iteration statements in ECMAScript, but we won’t cover them
here. Those of you wishing to discover the joys of switch…case, do…while and for…in will
have to hunt elsewhere.
ECMAScript Functions
As I’ve mentioned, the typical ECMAScript program is executed top down, and this restricts the
sophistication of our code somewhat. Well supposing we could…

• isolate a piece of code that performs a specific task
• give that code a descriptive name
• execute that code from arbitrary points in our program
• hand over some actual values to this piece of code

That would be handy, wouldn’t it? This is exactly what a
function
is for. Computer scientists
say:

Procedures (Functions) are the bricks of a structured programming language.
Calculating the length of a line with a simple function
Let’s have a look at an example of a short ECMAScript
function definition
. An SVG line
element has the attributes x1, y1, x2, y2 to specify its start and end points. Assume that we
frequently need to calculate the length of a line, so we decide to write a piece of code for exactly
that purpose.

Here is that code working on a html page. Please note, how we use the <script> element to
embed ECMAScript code.

<html>
<head>
<title> 'Calculate Length Of a Line' </title>
</head>
<body>
<h1> 'Calculate Length Of a Line' </h1>
<script type="text/ecmascript">
function LengthOfLine(x1, y1, x2, y2)
{
var dx = x2 - x1,
dy = y2 - y1,
lenSqr = dx*dx + dy*dy,
len = Math.sqrt(lenSqr);
return len;
}

var xpnt1 = 3, ypnt1 = 5, xpnt2 = 6, ypnt2 = 9;
var lineLength = 0;

lineLength = LengthOfLine(xpnt1, ypnt1, xpnt2, ypnt2);
document.write("Length of Line is " + lineLength); // 5 ..
</script>
</body>
</html>


Learn SVG Chapter 10 Scripting the DOM
9


Let’s have a look at how the function code is built-up.

1. Firstly, we start to implement an ECMAScript function using the keyword function.

2. Behind this we type the function name — here LengthOfLine. For this we are restricted to the
same rules while choosing a variable name, i.e. only letters, underscore and decimal digits (for
following characters) are allowed.

function LengthOfLine

3. Immediately following our new function name comes a pair of parenthesis that enclose a list of
comma-separated
formal arguments
(x1, y1, x2, y2). Those arguments are the names of
variables that can be used inside the function’s body.

4. That function body is always enclosed in curly braces {..} immediately following the function’s
head. Inside the functions body we define some variables (dx, dy, lenSqr and len) and initialize
them with some number expressions.

5. Now comes the Math. We build the difference of the x and y co-ordinates and calculate the
square of the line’s length lenSqr, according to Pythagoras' Theorem (c
2
= a
2 +
b
2
).

lenSqr = dx*dx + dy*dy


6. To get the actual length len of the line, we use an ECMAScript object’s function Math.sqrt, that
calculates the square root for us. We then hand this back to our calling program code using the
return statement.

len = Math.sqrt(lenSqr);
return len


Calling Functions
We can do exactly two things with a function. We can define it, i.e. write its code, and call it, i.e.
get its code executed.
Now, as we understand the common skeleton of a function as well as the structure of the above
example, it might be interesting to see how exactly a function is called.

var xpnt1 = 3, ypnt1 = 5, xpnt2 = 6, ypnt2 = 9;
var lineLength = 0;
lineLength = LengthOfLine(xpnt1, ypnt1, xpnt2, ypnt2); // 5 ..

The
function call
looks very similar to the function definition. Again we use the function name
LengthOfLine
to call the function that is to execute the code of the function definition’s body.

Since our function expects four arguments, we have to provide those, again enclosed in
parenthesis. We use the variables
xpnt1,ypnt1,xpnt2,ypnt2
here as
actual arguments
. The
function’s data type — yes, a function does have a data type — is of the same type as the
variable or expression we used with the
return
statement. With this we can use the function call
wherever we can use any variable or expression of that type.



Learn SVG Chapter 10 Scripting the DOM
10

I really urge you to become familiar with functions, not just because we will use them in detail
throughout the rest of this chapter, but because they really are handy things to have at your
disposal.
Function Guidelines
So what else should we know about functions?

• A function with a particular name must be defined only once.
• A particular function can be called arbitrarily from the rest of the program.
• A function can call other functions and itself.
• A function can have an arbitrary number of arguments, even none.
• A function with no arguments must be written with empty parenthesis
()
both at
definition and call.
• The number of function arguments and their type should match in definition and call.
• Local variables defined in a function’s body cannot be used outside that function.
• The functions formal arguments can be considered as local variables within the
function.

ECMAScript Objects
ECMAScript is a language that is designed for working inside of a host environment — the web
browser for example. The great benefit here comes with the fact that the host environment
exposes it’s own objects to ECMAScript, which can then access these objects for informational
purpose, but also for modifying them or even for creating new objects.
With this, ECMAScript can customize the facilities of any script hosting software system and
add powerful functionality.

As a prerequisite we need to know a little more about objects. We have already learned that we
can view an object as a bag full of embedded data – the Object’s
members
. If a member is a
variable, we call it an Object’s
property
. If a member is a function, it is called an Object’s
method
.

In order to access an object’s members we will use the dot-operator (
.
). With object variables we
need the special
null
value quite frequently. Variables that currently hold no object will be
assigned the
null
value to signal this.

Math.PI, list.length // Object’s properties ..
document.write(”hello”) // Object’s method call ..
var group = null; // variable with no value ..

But where do we meet objects?

• ECMAScript comes with a few intrinsic objects.
• The W3C’s DOM provides us with a lot of powerful objects.
• Java or ActiveX software components expose objects
• We can design our own objects with ECMAScript.

We won’t be designing our own objects here, but we can cover the first three options, so lets
begin with a look at the very common intrinsic ECMAScript objects. These consist of




Learn SVG Chapter 10 Scripting the DOM
11

• Array
• Boolean
• Date
• Function
• Global
• Math
• Number
• Object
• RegExp
• Error
• String

If you are experienced with other object oriented languages, you might ask, why we don’t talk
about classes here. So please note, that ECMAScript is a prototype-object based language, i.e.
everything is an object. ECMAScript in its current version 3.0 has no classes, the next generation
ECMAScript 4.0 will introduce them.

ECMAScript objects must be explicitly created. This is contrary to the other – so called intrinsic
– data types like Numbers, Strings, etc., that simply need to be defined. We create an object by
calling its constructor in combination with the
new
operator. The constructor is a special object
method – a creation method, that has the same name as the object itself and is always called with
the keyword
new.

var today = new Date(),
christmas = new Date(2001, 12, 24);

Note, that an object can have multiple constructors with different numbers of arguments.

Let’s focus here on the most commonly used objects —
Array
and
Math
.
The Array Object
Every programming language has something like an
array
data type. We can look at an array as a
variable that can encapsulate not just one, but multiple values. Thus an array is a useful container
element. Let’s illustrate this by example:

<html>
<head>
<title> The Array </title>
</head>
<body>
<h1> The Array </h1>
<script type="text/ecmascript">
var months = new Array(12); // create an array with 12 undefined elements
var days = new Array("mon","tue","wen","thu","fri","sat","sun"); // days of
week

document.write(days[0] + "<br>"); // "mon" .. first element of days ..
document.write(days[7] + "<br>"); // undefined ..
document.write(days.length + "<br>"); // 7 ..
months[1] = "feb"; // set months' 2nd value to "feb" ..
document.write(months + "<br>"); // ,feb,,,,,,,,,,,
</script>
</body>
</html>


We create an array object by calling its constructor. The
length
property can be used to
determine the actual length of an array, i.e. the number of items it actually holds. In order to
uniquely access one of these multiple values we need to append the so called
array operator

consisting of a pair of square brackets enclosing the
array index
.


Learn SVG Chapter 10 Scripting the DOM
12


We have to bear in mind that arrays in ECMAScript are zero-based. That is, the array index with
ECMAScript arrays always starts at zero (0) as in C, C++ and Java. Alongside this we should
remember that n-1 is the last index of an array holding n elements. So we can comfortably use a
for-statement to visit all elements of an array.
ECMAScript supports us with three types of Array constructors:


new Array(); create empty Array (no argument)
new Array(n); create Array of length n (one number argument)
new Array(x1,x2,..,xn); create Array of length n (n arguments)

The Math Object
Like other languages ECMAScript supplies the programmer with some prepackaged functions to
perform complex mathematical computations. Properly speaking these functions are methods of
a single Object – the
Math Object
. You need not to create that object via
new
operator. The
scripting engine provides it for us.

Some of these properties and methods are very useful, particularly for the geometrical problems
that often come along with SVG.

• Mathematical constants
o
Math.PI
ratio of a circles circumference to it’s diameter, approximately
3.1416.
o
Math.SQRT_2
the number value of the square root of two, approximately 1.4142.
o
Math.E
the number value of e, the base of the natural logarithm,
approximately 2.7183.

• Mathematical methods
o
Math.abs(n)
the absolute value of a number
n
.
o
Math.sin(n)
the sine of a number
n
.
o
Math.cos(n)
the cosine of a number
n
.
o
Math.atan2(y,x)
the angle in radians of a vector (x,y) with respect to the
positive x-axis.
o Math.sqrt(n)
the square root of a number
n
.


The Document Object Model

“The Document Object Model (DOM) is an application programming interface (API) for HTML and
XML documents. It defines the logical structure of documents and the way a document is accessed
and manipulated”. (W3C)

Put another way, the DOM is the W3C’s response to the HTML scripters cry for help with
DHTML clutter. However it is also more than this. It consists today of several modules.

When an XML document (remember every SVG file is a XML document) is loaded by an XML
parser, the parser builds a logical model of nodes from the structure of an XML document. With
these nodes the DOM provides a representation of the complete document stored in memory.

The nodes are objects with properties and methods for you, the programmer, to use. Since the
DOM specification only defines the objects properties and methods to be implemented by others,


Learn SVG Chapter 10 Scripting the DOM
13

those definitions are called
object interfaces
. We will use these interfaces extensively in this
chapter, but we won’t use them all, nor will we use and thus learn them completely.The ones
we’ll cover here are:


Node
Common interface for elements, attributes and text nodes

Document
Interface for the document object.

Element
Interface for element objects.

Text
Interface for text node objects.

Event
Interface for event objects.

The DOM tree
It’s helpful to look at the logical structure of nodes in memory as a tree-like structure of
elements. Each object in the DOM tree correlates uniquely to an entry in the XML file. Here is a
simple representation.

<svg>
|
|__ <defs>
| |__ <circle>
|
|__ <g>
| |__ <line>
| |__ <polyline>
| |__ <rect>
|
|__ <g>
|__ <g>
| |__ <text>
| |__ <path>
|
|__ <use>
|__ <ellipse>
|__ <text>

As you can see, the root node of the tree is the
<svg>
document element itself. Below the root
node there are two branches — the
<g>
elements — which also have branches or finally leaf
nodes. But we will not talk about branches and leafs. We will use a parent/child/sibling
relationship instead. Thus the
<ellipse>
element has two siblings, a preceding
<use>
element
and a
<text>
sibling element following it. The
<ellipse>
element also has exactly one parent —
a
<g>
element — and no child elements. With these terms we can uniquely and graphically
describe the relationship an element has to its neighbours.

We also have to bear in mind that everything in the DOM tree is basically a
Node
object. So if we
meet an
Element
object anywhere, we know that we are able to use its
Element
properties and
Element
methods. But since we know now that it is also a
Node
object, it implements the
Node

properties and
Node
methods also. So let’s have a look what these are.
Climbing our Tree
As we’ve learned, every element of the DOM tree, whatever it may be exactly, is a
Node
. Since
we know the tree, we can also navigate along it. For this we need only some navigational
properties of the
node
and
document
interface, and I have summarized them below.


Document navigational property:
Value

Property

Description

Element
documentElement
the document object belonging to this node.



Learn SVG Chapter 10 Scripting the DOM
14


Node informational properties:
Value

Property

Description

String
nodeName
the name of the node (its tag name).

String
nodeValue
the value of the node depending of its type. Mostly null, for text nodes the containing text.

Number
nodeType
ELEMENT_NODE = 1;
ATTRIBUTE_NODE = 2;
TEXT_NODE = 3;
CDATA_SECTION_NODE = 4;
ENTITY_REFERENCE_NODE = 5;
ENTITY_NODE = 6;
PROCESSING_INSTRUCTION_NODE = 7;
COMMENT_NODE = 8;
DOCUMENT_NODE = 9;
DOCUMENT_TYPE_NODE = 10;
DOCUMENT_FRAGMENT_NODE = 11;
NOTATION_NODE = 12;

Node navigational properties:
Value

Property

Description

Node
ownerDocument
the document object belonging to this node.

Node
ParentNode
the parent node of this node.

Array
ChildNodes
an array of the child nodes of this node.

Node
FirstChild
the first child node of his node.

Node
LastChild
the last child node of his node.

Node
previousSibling
the node preceding this node.

Node
nextSibling
the node following this node.


From trees to trains
To illustrate the DOM tree in action, I will make use of a train example. Here is the model
Figure 10-1. The train example

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/SVG/DTD/svg10.dtd">
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" >
<defs>
<g id="carriage">
<line x2="150" y2="0" stroke="gray" stroke-width="10" stroke-
linecap="round" />
<circle cx="20" cy="17" r="10" fill="lightgray" stroke="black" />
<circle cx="130" cy="17" r="10" fill="lightgray" stroke="black" />
</g>
</defs>
<g id="train">
<g id="engine" transform="translate(390, 300)" >
<use xlink:href="#carriage" x="0" y="0" />


Learn SVG Chapter 10 Scripting the DOM
15

<polyline id="cabin" points="150,0 150,-60 90,-50 90,0"
stroke="gray" stroke-width="10" stroke-linejoin="round" fill="gray"
/>
<rect x="10" y="-90" width="70" height="80" fill="darkgreen" stroke="black"
/>
</g>
<g id="wagon1" transform="translate(220,300)">
<use xlink:href="#carriage" x="0" y="0" />
<circle cx="40" cy="-42" r="20" stroke="darkred" stroke-width="30"
fill="none" >
<circle cx="110" cy="-42" r="20" stroke="steelblue" stroke-width="30"
fill="none" />
</g>
<g id="wagon2" transform="translate(50,300)">
<use xlink:href="#carriage" x="0" y="0" />
<text x="5" y="-10" font-family="Verdana" font-size="120" fill="purple" >
A</text>
<rect id="bluebox" x="90" y="-100" width="60" height="90" fill="navy"
stroke="black" />
</g>
</g>
</svg>

So, our train consists of an engine with two wagons and some cargo — a big letter, two boxes and two coils.
The carriages of the engine and the wagons are identical. So we implement it as a group in the <defs>
section and reuse it via <use> elements. The wagons and the engine are themselves defined as groups, which
include also the cargo. Engine and wagons are embedded in a group called train..
A Model Train
We don’t need the entire SVG code for illustrating simple navigation, so we use the reduced
model representation here.

<svg>
|__ <defs>
| |__ <g id=”carriage”>
|
|__ <g id=”train”>
|
|__ <g id=”engine”>
| |__ <use href=”carriage”>
| |__ <polyline> // cabin
| |__ <rect> // green box
|
|__
<g id=”wagon1”> // we start here !
| |__ <use href=”carriage”>
| |__ <circle> // red coil
| |__ <circle> // blue coil
|
|__ <g id=”wagon2”>
|__ <use href=”carriage”>
|__ <text> // big letter
|__ <rect> // blue box

Moving around our train
OK. Lets try navigating around the train, with the general goal of accessing the blue box, a child
element of
wagon2.
As we will discuss different possibilities of entering the DOM tree later in
this chapter, let’s assume now for simplicity that we have an ECMAScript variable
wagon1
that
still contains the node of the
wagon1
group.


var wagon1; // magically contains the wagon1 node object ..

1. To reach the document from here, i.e. to jump to the top level, you simply type


var doc = wagon1.ownerDocument;


Learn SVG Chapter 10 Scripting the DOM
16


wagon1.
ownerDocument
<svg>
|_ <defs>
| |_ <g id="carriage">
|
|_ <g id="train">
|
|_ <g id="engine">
| |_ <use href="carriage">
| |_ <polyline> // cabin
| |_ <rect> // green box
|
|_ <g id="wagon1">
| |_ <use href="carriage">
| |_ <circle> // red coil
| |_ <circle> // blue coil
|
|_ <g id="wagon2">
|_ <use href="carriage">
|_ <text> // big letter
|_ <rect> // blue box
<svg>
|_ <defs>
| |_ <g id="carriage">
|
|_ <g id="train">
|
|_ <g id="engine">
| |_ <use href="carriage">
| |_ <polyline> // cabin
| |_ <rect> // green box
|
|_ <g id="wagon1">
| |_ <use href="carriage">
| |_ <circle> // red coil
| |_ <circle> // blue coil
|
|_ <g id="wagon2">
|_ <use href="carriage">
|_ <text> // big letter
|_ <rect> // blue box


This is a very useful feature of the DOM – the ability to access the
Document
object from an
arbitrary tree node in a simple manner.

2. Now assume you are still standing on the middle wagon and want to jump from here onto the
engine. For this you use


var engine = wagon1.previousSibling;

wagon1.
previousSibling
<svg>
:
|_ <g id="train">
|
|_ <g id="engine">
| |_ <use href="carriage">
| |_ <polyline> // cabin
| |_ <rect> // green box
|
|_ <g id="wagon1">
| |_ <use href="carriage">
| |_ <circle> // red coil
| |_ <circle> // blue coil
:
<svg>
:
|_ <g id="train">
|
|_ <g id="engine">
| |_ <use href="carriage">
| |_ <polyline> // cabin
| |_ <rect> // green box
|
|_ <g id="wagon1">
| |_ <use href="carriage">
| |_ <circle> // red coil
| |_ <circle> // blue coil
:

3. Here you realize that you should have jumped onto the last wagon instead. So you type


var wagon2 = engine.nextSibling.nextSibling;

engine.
nextSibling.
nextSibling
<svg>
:
|_<g id="train">
|
|_<g id="engine">
<svg>
:
|_<g id="train">
|
|_<g id="engine">
<svg>
:
|_<g id="train">
|
|_<g id="engine">


Learn SVG Chapter 10 Scripting the DOM
17

| |_<use
href="carriage">
| |_<polyline> // cabin
| |_<rect> // green box
|
|_<g id="wagon1">
| |_<use
href="carriage">
| |_<circle> // red coil
| |_<circle> // blue
coil
|
|_<g id="wagon2">
|_<use
href="carriage">
|_<text> // big letter
|_<rect> // blue box
| |_<use
href="carriage">
| |_<polyline> // cabin
| |_<rect> // green box
|
|_<g id="wagon1">
| |_<use
href="carriage">
| |_<circle> // red coil
| |_<circle> // blue
coil
|
|_<g id="wagon2">
|_<use
href="carriage">
|_<text> // big letter
|_<rect> // blue box
| |_<use
href="carriage">
| |_<polyline> // cabin
| |_<rect> // green box
|
|_<g id="wagon1">
| |_<use
href="carriage">
| |_<circle> // red coil
| |_<circle> // blue
coil
|
|_<g id="wagon2">
|_<use
href="carriage">
|_<text> // big letter
|_<rect> // blue box

You see that
previousSibling
and
nextSibling
are a comfortable way to step along the nodes
of a certain tree level. Now let’s remember our task to check the blue box on
wagon2
.

4. To achieve this, you need to navigate down one tree level and then walk on that level again.


var blueBox = wagon2.firstChild.nextSibling.nextSibling;

wagon2.firstChild
nextSibling.
nextSibling
<svg>
:
|_<g id="train">
:
|_<g id="wagon2">
|_<use
href="carriage">
|_<text> // big letter
|_<rect> // blue box
<svg>
:
|_<g id="train">
:
|_<g id="wagon2">
|_<use
href="carriage">
|_<text> // big letter
|_<rect> // blue box
<svg>
:
|_<g id="train">
:
|_<g id="wagon2">
|_<use
href="carriage">
|_<text> // big letter
|_<rect> // blue box
or alternatively


var blueBox = wagon2.lastChild;

wagon2.
lastChild
<svg>
:
|_<g id="train">
:
|_<g id="wagon2">
|_<use href="carriage">
|_<text> // big letter
|_<rect> // blue box
<svg>
:
|_<g id="train">
:
|_<g id="wagon2">
|_<use href="carriage">
|_<text> // big letter
|_<rect> // blue box


We notice here that there are two possible ways in order to access the blue box, which is a child
of
wagon2
. We can step down at the beginning of the child list with
wagon2.firstChild
and then
walk forward via two
nextSibling
calls, or we jump at the child list’s end via
wagon2.lastChild
with a following step back to our target by
previousSibling
.

Now, having discovered that all is well with the blue box, we might need to hurry back to the
engine’s cabin.


Learn SVG Chapter 10 Scripting the DOM
18


5. For this we’ll need to go up one level, stepping back onto the engine. From there we can step
down to the engine’s child nodes then shimmy along until we reach the cabin, as shown below.


var cabin =
blueBox.parentNode.previousSibling.previousSibling.firstChild.nextSibling;

blueBox.parentNode.
previousSibling.previousSiblin
g.
firstChild.nextSibling
<svg>
:
|_<g id="train">
|
|_<g id="engine">
| |_<use
href="carriage">
| |_<polyline> // cabin
| |_<rect> // green box
|
|_<g id="wagon1">
| |_<use
href="carriage">
| |_<circle> // red
coil
| |_<circle> // blue
coil
|
|_<g id="wagon2">
|_<use
href="carriage">
|_<text> // big
letter
|_<rect> // blue box
<svg>
:
|_<g id="train">
|
|_<g id="engine">
| |_<use href="carriage">
| |_<polyline> // cabin
| |_<rect> // green box
|
|_<g id="wagon1">
| |_<use href="carriage">
| |_<circle> // red coil
| |_<circle> // blue coil
|
|_<g id="wagon2">
|_<use href="carriage">
|_<text> // big letter
|_<rect> // blue box
<svg>
:
|_<g id="train">
|
|_<g id="engine">
| |_<use
href="carriage">
| |_<polyline> // cabin
| |_<rect> // green box
|
|_<g id="wagon1">
| |_<use
href="carriage">
| |_<circle> // red coil
| |_<circle> // blue
coil
|
|_<g id="wagon2">
|_<use
href="carriage">
|_<text> // big letter
|_<rect> // blue box

Remember that we can always go up one level with
parentNode
— we don’t need to go to the
beginning or end of the list of siblings first. This means that we have another method available to
access the cabin.

6. We can step up two levels to the
train
node and go from here two levels down again instead of
walking along the train’s children.

var cabin = blueBox.parentNode.parentNode.firstChild.firstChild.nextSibling;

Up to here we have discovered how we can walk along the entire tree of our SVG example
document. However, imagine that our train consists of many more wagons and we wish to
navigate to one of them somewhere in the middle from the engine room. Rather than typing
numerous
nextSibling instructions,
W3C provides us with a time and code-saving alternative.

7. This alternative consists of going up one level in the tree — to the
train
node — and asking it
to supply us with an array of all children. This is achieved via


var wagons = engine.parentNode.childNodes;

And with this we can go directly to the 142th wagon by simply using


var wagon142 = wagons.item(141);



Learn SVG Chapter 10 Scripting the DOM
19

Just to drum it in one more time, the ECMAScript and DOM arrays are zero-based. Because of this
the array index 141 points to the 142th array element.
On the basis of this I’d like to present you a small ECMAScript function, that allows us to visit
the total subtree of a particular node. Note that this function is recursive, that is, a function that
calls itself.

function VisitChildren(node)
{
for (var child = node.firstChild; child != null; child = child.nextSibling)
{
// do something with your child here (except removing) ..
if (child.hasChildNodes())
VisitChildren(child);
}
}

You might find this function beneficial for your own DOM scripting projects.

Preparing for a working Example

Now let’s build a complete working SVG document with scripting here. For this we first have to
resolve two issues.
• We need some event handling, which we will cover not until later in this chapter.
• We have to deal with additional nodes, that I did not mention previously – the whitespace
nodes.

No problem with the event handling here. We will just use it, I will explain it to you later. Dealing
with the whitespace nodes is less trivial. So what are these whitespace nodes?



Every XML document can have text nodes – nodes consisting completely of text. Furthermore that
text may consist purely of spaces (∙) or newlines (¶). Such a series of spaces and newlines will be
interpreted by an XML parser as a text node – we call it whitespace node.

Understanding that, we can see now several whitespace nodes in the above SVG fragment. The
problem arises, if we want to get the next node of the line element. nextSibling won’t provide us
with the circle element, but with a whitespace node.

So what shall we do?
• Properly deal with the whitespace text nodes.
• Remove all whitespace text nodes, immediately after the document has completely loaded.

The previous node navigation examples ignored these whitespace nodes for simplicity. In order to
get that code to work, we will decide for the second solution – removing all whitespace text nodes.
For this I provide you with a utility function, that does exactly that.



Learn SVG Chapter 10 Scripting the DOM
20

function RemoveWhiteSpaceChildNodesOf(node)
{
if (node != null)
{
var child = node.lastChild;
while (child != null)
{
if (child.nodeType == 3 && child.nodeValue.match(/\S/) == null)
{
var previous = child.previousSibling;
child.parentNode.removeChild(child);
child = previous;
}
else
{
RemoveWhiteSpaceChildNodesOf(child);
child = child.previousSibling;
}
}

}
}

You might not understand that function completely yet (it is similar to the VisitChildren function),
but since we know, how to call a function and as we consider this a quite useful function, we will
simply use it. Since we will reuse this function in several SVG documents, we decide to put it into
an external file
RemoveWhiteSpace.js
. We can embed this function then simply by the <script>
element.

<script type="text/ecmascript" xlink:href="RemoveWhiteSpace.js" />

Please note, that we have to deal with whitespace text node handling only under certain conditions.

When using the Node object’s navigational properties (firstChild, nextSibling, etc.), we
have to consider the appearance of whitespace text nodes.
With that here is our last node navigation example as a complete working document.

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/SVG/DTD/svg10.dtd">
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg"
onload="Init(evt);">
<script type="text/ecmascript" xlink:href="RemoveWhiteSpace.js"/>
<script type="text/ecmascript">
<![CDATA[
function Init(evt)
{
RemoveWhiteSpaceChildNodesOf(evt.target.ownerDocument.documentElement);

var blueBox = evt.target.ownerDocument.getElementById("bluebox");
var cabin =
blueBox.parentNode.parentNode.firstChild.firstChild.nextSibling;
window.alert(cabin.nodeName);
]]>
</script>
<defs>
<g id="carriage">
<line x2="150" y2="0" stroke="gray" stroke-width="10" stroke-
linecap="round" />
<circle cx="20" cy="17" r="10" fill="lightgray" stroke="black" />
<circle cx="130" cy="17" r="10" fill="lightgray" stroke="black" />
</g>
</defs>
<g id="train">
<g id="engine" transform="translate(390, 300)" >
<use xlink:href="#carriage" x="0" y="0" />
<polyline id="cabin" points="150,0 150,-60 90,-50 90,0"


Learn SVG Chapter 10 Scripting the DOM
21

stroke="gray" stroke-width="10" stroke-linejoin="round" fill="gray"
/>
<rect x="10" y="-90" width="70" height="80" fill="darkgreen" stroke="black"
/>
</g>
<g id="trailer1" transform="translate(220,300)">
<use xlink:href="#carriage" x="0" y="0" />
<circle cx="40" cy="-42" r="20" stroke="darkred" stroke-width="30"
fill="none" />
<circle cx="110" cy="-42" r="20" stroke="steelblue" stroke-width="30"
fill="none" />
</g>
<g id="trailer2" transform="translate(50,300)">
<use xlink:href="#carriage" x="0" y="0" />
<text x="5" y="-10" font-family="Verdana" font-size="120" fill="purple"
>A</text>
<rect id="bluebox" x="90" y="-100" width="60" height="90" fill="navy"
stroke="black" />
</g>
</g>
</svg>

Please note:
• The
onload
event handler, that causes the
Init(evt)
function to be called, when the
document is completely loaded. (Not until then the DOM tree is fully constructed!)
• The use of the
<script type="text/ecmascript">
element. We can in general safely omit
the
type="text/ecmascript"
attribute, since
="text/ecmascript"
is always the default
value.
• The
<![CDATA[ .. ]]
section, that causes any XML parser not to interpret the enclosed text
as markup. This is necessary, if we safely want to use characters as ‘
<
‘ with a particular
meaning in XML inside of script.

Sophisticated Searching
The step by step method of navigating the DOM tree is quite useful, if we

• Walk short distances in any direction of the tree.
• Know the exact nature of the tree structure.
• Need to walk the entire tree.

Of course, this is not normally the case. Much more often we are searching a particular node in
the tree and want to jump directly onto it. With that we need to have some information about
that node. Having this we can consult the
Document
object that offers some useful services
regarding our problem.
Document Searching
Let’s have a look at the methods we can use to jump directly to specified elements. They are
listed below.

Document navigational property:
Return Value

Method

Description

Array
getElementsByTagName(tagName)
An array of element nodes with this name.

Element
getElementsById(elementId)
The element with an Id that has this string value.


1. Now, as an example, let’s say we know the node we wish to visit is a
<g>
element. Still sitting
on the engine, we code



Learn SVG Chapter 10 Scripting the DOM
22


var elements = engine.ownerDocument.getElementsByTagName(”g”);

which will yield an array, hopefully filled with all group elements.

2. Now we can run over the list using our new companion, the
for
loop.

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/SVG/DTD/svg10.dtd">
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg"
onload="Init(evt);">
<script type="text/ecmascript" xlink:href="RemoveWhiteSpace.js"/>
<script type="text/ecmascript">
<![CDATA[
function Init(evt)
{
RemoveWhiteSpaceChildNodesOf(evt.target.ownerDocument.documentElement);

var engine = evt.target.ownerDocument.getElementById("engine");
var elements = engine.ownerDocument.getElementsByTagName("g");
for (var i=0; i < elements.length; i++)
window.alert("Element " + i + " of type: " +
elements.item(i).nodeName);
}
]]>
</script>
<!-- train's geometry goes here -->
</svg>


With this we will receive the following output:


Element 0 of type: g
Element 1 of type: g
Element 2 of type: g
Element 3 of type: g
Element 4 of type: g

Somewhat astonished you look at the train document tree above and count five
<g>
elements
including the
carriage
in the
<defs>
section. Fine, now we only need to select our target
element out of five groups. Maybe we know a little more of our group — the colour perhaps, or
any other attribute.

But hold on. There was another method offered by the
document
object. With this we come to
the solution most often used in real code. Provided that we marked our target element with a
unique identifier via the
id
attribute, we can use the
document
object’s
getElementById
method
to jump directly to it.

3. Still sitting on your engine you decide to visit the most distant wagon and type


var mostDistantWagon = engine.ownerDocument.getElementById(“wagon2”);

Voila, here it is. We now have an equally simple and powerful solution. Any element in the tree
can beg the always accessible
document
object to do it a favour and perform a quick search for a
particular element with a known id and every element we want to reach via this method needs to
have an id in the first place. A prerequisite for this is in general:

Id attribute values must be unique across the whole xml document and should obey the syntactical
rules of ECMAScript variables.


Learn SVG Chapter 10 Scripting the DOM
23

I emphasise this, though a matter of course as for program variables, since
• potential scripting code relies heavily on this.
• current xml parsers may not complain about this (future parsers will).
• some xml-generating software may create duplicate or invalid id values.
Accessing Attributes
I promised to show you how we can access an element’s attributes. This is not very mysterious.
There are two different methods of the Element object to help with attribute access.

Element’s attribute access methods:

Return Value

Method

Description

String
getAttribute(attrName)
The value of the attribute with the name attrName as a
string.

void
setAttribute(attrName,
newValue)
set the attribute with the name attrName value to
newValue.


Firstly I’d like to focus on the
getAttribute
method in particular.

Let’s remember the problem. We had an array full of
<g>
elements and we needed to find the
particular one with the id value
trailer2
. We temporarily neglect the fact that we solved this
search problem very elegantly using the
getElementById
method. With our new knowledge
about attributes we could iterate over the array and compare every
<g>
element’s
id
value with
the string
”trailer2”
.

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/SVG/DTD/svg10.dtd">
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg"
onload="Init(evt);">
<script type="text/ecmascript" xlink:href="RemoveWhiteSpace.js"/>
<script type="text/ecmascript">
<![CDATA[
function Init(evt)
{
RemoveWhiteSpaceChildNodesOf(evt.target.ownerDocument.documentElement);

var engine = evt.target.ownerDocument.getElementById("engine");
var groups = engine.ownerDocument.getElementsByTagName("g");
var wagon = null; // null as long as you didn't find it ..
var currentGroup = null;

for (var i=0; wagon == null && i < groups.length; i++)
{
currentGroup = groups.item(i);
if (currentGroup.getAttribute("id") == "wagon2") // found :-)
wagon = currentGroup;
}

if (wagon != null)
window.alert("wagon with id = '" + wagon.getAttribute("id") + "'
found!");
}
]]>
</script>
<!-- train's geometry goes here -->
</svg>

The last line of your code should finally produce the output:


wagon with id = 'wagon2' found!



Learn SVG Chapter 10 Scripting the DOM
24

This technique of selecting elements by looking at the attributes is especially useful, when there
is no
id-
attribute that can be used for the
getElementById
method.
Dynamic elements
Now we can navigate quite elegantly through the DOM tree, it’s about time we learned how to
modify the attributes we find along the way. The Element’s method
setAttribute
is provided
by the DOM for exactly that purpose.
Altering Dynamic Elements
Your mobile phone rings and the boss tells you that you are carrying the wrong letter — your
train is supposed to be carrying a Y instead of an A.. Furthermore you picked up the wrong box.
The correct one is twice as high as yours and it’s colour isn’t green but gold. With your new
knowledge about DOM modification you decide not to drive back to the storage to change the
cargo. You would rather try to manipulate your load dynamically.

1. Modifying the box is quite simple. All you need to do is change the corresponding attributes
accordingly.

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/SVG/DTD/svg10.dtd">
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg"
onload="Init(evt);">
<script type="text/ecmascript" xlink:href="RemoveWhiteSpace.js"/>
<script type="text/ecmascript">
<![CDATA[
function Init(evt)
{
RemoveWhiteSpaceChildNodesOf(evt.target.ownerDocument.documentElement);

var engine = evt.target.ownerDocument.getElementById("engine");
var greenBox = engine.lastChild,
greenBoxHeight = parseFloat(greenBox.getAttribute("height")),
greenBoxY = parseFloat(greenBox.getAttribute("y"));

greenBox.setAttribute("height", 2*greenBoxHeight);
greenBox.setAttribute("y", greenBoxY - greenBoxHeight);
greenBox.setAttribute("fill", "gold");
}
]]>
</script>
<!-- the train's geometry goes here -->
</svg>

2. Still sitting in the engine’s cabin you take a look behind you to see what happened.



Learn SVG Chapter 10 Scripting the DOM
25


Figure 10-2. Modified train

Wow, it worked! Let’s look at the code again.

3. Note that we passed
Number
objects as well as a
String
object as the second argument to the
setAttribute method. Contrary to the use of getAttribute we don’t concern ourselves
with the type. Any necessary type conversions are performed by the setAttribute method
internally. Please note, that the green rect was anchored at the top and its height actually
determines the position of the rect’s bottom. Thus to compensate for increasing the height, you
must reduce the y.

So our next task is to change our A to a Y. To achieve this we will jump to the last wagon. There
is no problem with that, since we have it’s
id
. Then we decide to query the
wagon2
element for
an array with all
<text>
elements, which hopefully will be filled with exactly one item.

4. Having this then, you argue, the A must be the one and only child node. It’s type must be a text
node, whose value can be changed then to a Y. Sounds good, so you start coding.

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/SVG/DTD/svg10.dtd">
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg"
onload="Init(evt);">
<script type="text/ecmascript" xlink:href="RemoveWhiteSpace.js"/>
<script type="text/ecmascript">
<![CDATA[
function Init(evt)
{
RemoveWhiteSpaceChildNodesOf(evt.target.ownerDocument.documentElement);
var engine = evt.target.ownerDocument.getElementById("engine");

var wagon2 = engine.ownerDocument.getElementById("trailer2");
var texts = wagon2.getElementsByTagName("text"); // all text child
elements ..

if (texts.length > 0) // ok, it must be our 'A'
..
{


Learn SVG Chapter 10 Scripting the DOM
26

var letter = texts.item(0).firstChild; // text node as child of <text>
element
letter.nodeValue = "Y"; // whose value we need to
change.
}
else // oops
window.alert("I lost a letter on the way - or got some more!");
}
]]>
</script>
<!-- the train's geometry goes here -->
</svg>

It’s a bit strenuous to look around the big golden box just to see the result.


Figure 10-3. Changing letter

Now we have learned how to change attributes and textual content of SVG elements, and with this
armoury are able to put right any potential faults in our document.
Removing Elements and Attributes
We’ll kick off here with removal, as with the DOM, as in real life, it is always easier to destroy
something than to build it. I’d like to introduce you to two methods.

Node’s remove method:
Node removeChild(node)
remove this node from the tree.

Element’s attribute removal method:
Node removeAttribute(attr)
remove the element’s attribute.

Before you are urged now to wildly remove anything that comes up, please bear these rules in
mind:

Any node - and with this any element - can be removed solely by its parent.
An attribute can be removed only by the element it belongs to.


Learn SVG Chapter 10 Scripting the DOM
27


As always we’ll understand this best by example. Remember that you are still sitting in the
engine’s cabin. Since you are going to arrive at your first customer, who ordered the coils, you
have a good chance now to deliver the cargo via the DOM. You are quite practised now with
algorithms, so you present your plan:

• Go from the
engine
to
wagon1
.
• Pick the rearward coil from the wagon combining
lastChild
and the
removeChild

method.
• Pick the forward coil from the trailer using
lastChild.previousSibling
with the
removeChild
method.

1. With this in mind you immediately start programming the code:

var wagon1 = engine.ownerDocument.getElementById("wagon1");

wagon1.removeChild(wagon1.lastChild); // remove blue coil ..
wagon1.removeChild(wagon1.lastChild); // remove red coil ..

2. Happy with this concise bit of coding, you look at the result:


Figure 10-4. Remove objects

It works. But I also want to show you, that removing elements from the tree is a tricky task.

3. Let’s have a look at the
wagon1
subtree before removing any coil.

|__ <g id=”wagon1”>
| |__ <use href=”carriage”>
| |__ <circle> // red coil
| |__ <circle> // blue coil
|
4. After the first removal of the blue coil the resulting tree looks like this:

|__ <g id=”wagon1”>
| |__ <use href=”carriage”>


Learn SVG Chapter 10 Scripting the DOM
28

| |__
<circle> // red coil
|
wagon1.removeChild(wagon1.lastChild.previousSibling);

ling;
letterY.removeAttribute("fill"); // remove purple paper ..
wagon1
<use href=”carriage”>
|
6. parent from
ginning to end

parent.removeChild(parent.firstChild);
parent.removeChild(parent.lastChild);
ny element removal changes
the tree structure. Subsequent actions operate on that new structure.
setAttribute
ea od:
lue


Now it is possible to see what would have happened when you mistakenly called

in order to remove the red coil. It was the last child then, so you would have removed the
arriage.
c

5. Lets also remove the purple paper from the big Y letter on the last trailer, trying out
.
removeAttribute

var letterY =
engine.ownerDocument.getElementById("wagon2").firstChild.nextSib


Now we see what we all initially expected, alongside the true colour of the Y letter. The
subtree now looks like this:


|__ <g id=”wagon1”>
| |__


Finally, here’s a handy bit of code designed to remove all child elements of any
be


while (parent.firstChild != null)


. Or from the end to the beginning
7


while (parent.firstChild != null)


Whenever you remove elements, especially in loops, remember that a
Cr
eating Elements
Lets be a bit more constructive now. Initially I’ll present you with some useful methods that
we’ll need for creating elements and for adding them to the tree. Attributes are not covered here,
as we have already learned how to create them by the method.

Document’s cr

tion meth
Return Va
Method

Description

E
lement
createElement(tagName)
Creates a new element of type tagName.


Node’s creatio

n method:
Value
Return
Method

Description

N
ode
cloneNode(deep)
Create a clone of itself.


Node’s inserti
ng metho
ds:
Value
Return

Method

Description

Node
insertBefore(node,
child)
Insert node node before child node child into the c
hildren list.

Node
appendChild(node)
Append node node at the end of the children list.

Node
replaceChild(node, child)
Replace child node child with node node in the children list.



Learn SVG Chapter 10 Scripting the DOM
29


The remarkable
Document
object is not only DOM’s search engine, but also an element factory.
So if we need a new element, we have to find the
Document
object — which is easy achieved by
using the
ownerDocument
property from anywhere in the tree — and simply use its
createElement
method. As an argument we pass “
rect
”, “
circle
” or the name of whichever
element we wish to create, to that method.

An arbitrary
Node
object also has some restricted creation capability — the ability to create a
clone of itself. The single argument
deep
is of type
Boolean
and controls the behaviour, when the
cloning node has child nodes. If
deep
has a value of
true,
a deep copy will be performed, i.e. all
child nodes will get cloned also. With this feature we can create a copy of a whole subtree in a
very simple manner. The value
false
of the
deep
argument will result in a shallow copy, a copy
of only the cloning parent node without its children.
DOM Expansion
Usually we will expand the DOM tree with these two consecutive steps:

1. Create a new node from the
Document
object’s
createElement
method.
2. Add the new node to the tree using one of the
Node
object’s inserting methods.

But we can also perform quite a special action. If we want to move an existing node from the
tree onto another location in the same tree we can use the
Node
object’s inserting methods for
that too. The node to move is removed from the tree first and then reinserted again.
Back on the Train
So now you should know enough to try this out so let’s return to our train delivery example. You
have had a conversation with a customer — that one with the coils. He asked you to transport
back an olive pyramid and a defect wagon belonging to your company. So your objectives now
appear like this.

• Pick the blue box from the last wagon and put it onto the middle one, as you want to
have the defect wagon at the end of your train.
• Do the same with the Y letter.
• Take the defect wagon.
• Load up the defect wagon onto the last – now empty – wagon.
• Take the olive pyramid.
• Put the pyramid on top of the defect wagon.

1. First things first then, you’ll need to code the following to move goods from the end wagon to
the middle.

var wagon1 = engine.ownerDocument.getElementById("wagon1"),
wagon2 = engine.ownerDocument.getElementById("wagon2");

wagon1.appendChild(wagon2.lastChild); // move the blue box to the middle trailer.
wagon1.appendChild(wagon2.lastChild); // move the Y letter to the middle trailer.

So — “why have the box and the Y letter changed their location?” the customer asks. You didn’t
modify any coordinates. You explain patiently to this SVG amateur, that the wagons are
designed as
<g>
elements with identical local coordinate systems. Their different positions come
from suitable translations via the
transform
attribute. So the coordinates inside all wagon groups
are the same.


Learn SVG Chapter 10 Scripting the DOM
30



Figure 10-5. Change location of objects

2. Now onto the next points of the plan — loading the defect wagon onto the now empty one.
Well, the best strategy to get a defect wagon seems to be by simply cloning the last empty
wagon.

var wagon1 = engine.ownerDocument.getElementById("wagon1"),
wagon2 = engine.ownerDocument.getElementById("wagon2");

wagon1.appendChild(wagon2.lastChild); // move the blue box to the middle wagon.
wagon1.appendChild(wagon2.lastChild); // move the Y letter to the middle wagon.

var defectWagon =
wagon
2.cloneNode(true);
defectWagon.setAttribute("transform", "translate(0,-35)");

wagon2.appendChild(defectWagon);

3. Now we must move onto creating the olive pyramid — best as a
<polygon>
element — and
putting it onto the defect wagon before looking at the result.

var wagon1 = engine.ownerDocument.getElementById("wagon1"),
wagon2 = engine.ownerDocument.getElementById("wagon2");

wagon1.appendChild(wagon2.lastChild);
wagon1.appendChild(wagon2.lastChild);

var defectWagon = wagon2.cloneNode(true);
defectWagon.setAttribute("transform", "translate(0,-35)");

var pyramid = engine.ownerDocument.createElement("polygon");
pyramid.setAttribute("points", "5,-10 145,-10 75,-100");
pyramid.setAttribute("fill", "olive");
defectWagon.appendChild(pyramid);

wagon2.appendChild(defectWagon);


4. With this you are finished and can cast a look at the result and the impressed customer.



Learn SVG Chapter 10 Scripting the DOM
31


Figure 10-6. Adding new objects

With this we have learned how easy it is to create new SVG elements and add them to an
existing DOM tree. You should also know, that the
Document
object offers a lot more creation
methods. You can even create a new
Document
object using the
DOMImplementation
object. But I
won’t cover all this here.

We should always remember, that node creation and deletion basically work upon internal memory
operations. Those operations are usually expensive in runtime and memory so I recommend that if a
task can be accomplished by Node creation/removal or by attribute modification, always prefer the
modification strategy for performance reasons.
A Quick Trick
Related to this notion I’ll let you in on something of a trick, that could well be useful in practise.
Assume you need to have one or more elements to be appearing and disappearing periodically.
Instead of removing and reinserting them with every cycle, it is far more advantageous to let
them stay in the DOM tree and control only the elements visibility. We can achieve this with …

wagon1.setAttribute("visibility","hidden");


As far as single line functionality goes, it’s pretty hard to beat!

Finally, I want to present you the total source of the track example, with the individual actions
organized as functions.

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/SVG/DTD/svg10.dtd">
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg"
onload="Init(evt);">
<script type="text/ecmascript" xlink:href="RemoveWhiteSpace.js"/>
<script type="text/ecmascript">
<![CDATA[
function Init(evt)
{


Learn SVG Chapter 10 Scripting the DOM
32

RemoveWhiteSpaceChildNodesOf(evt.target.ownerDocument.documentElement);
var engine = evt.target.ownerDocument.getElementById("engine");

FindTrailer2ByAttribute(engine);
MakeGreenBoxGolden(engine);
ChangeAToY(engine);
RemoveCoils(engine);
MoveBoxAndLetterAndCreateDefectWagonAndPyramid(engine);
}

function FindTrailer2ByAttribute(engine)
{
var groups = engine.ownerDocument.getElementsByTagName("g");
var wagon = null; // null as long as you didn't find it ..
var currentGroup = null; // .. used in loop ..

for (var i=0; wagon == null && i < groups.length; i++)
{
currentGroup = groups.item(i);
if (currentGroup.getAttribute("id") == "wagon2") // found :-)
wagon = currentGroup;
}
if (wagon != null)
window.alert("group found with id: " + wagon.getAttribute("id"));
else
window.alert("no group found with id 'wagon2'");
}

function MakeGreenBoxGolden(engine)
{
var greenBox = engine.lastChild,
greenBoxHeight = parseFloat(greenBox.getAttribute("height")),
greenBoxY = parseFloat(greenBox.getAttribute("y"));

greenBox.setAttribute("height", 2*greenBoxHeight);
greenBox.setAttribute("y", greenBoxY - greenBoxHeight);
greenBox.setAttribute("fill", "gold");
}

function ChangeAToY(engine)
{
var wagon2 = engine.ownerDocument.getElementById("wagon2");
var texts = wagon2.getElementsByTagName("text");

if (texts.length > 0) // ok, it must be our 'A' ..
{
var letter = texts.item(0).firstChild;