Using script to find an object and change its attributes

We'll show two different but effective ways to do this. First we'll begin with the standard template discussed earlier and change the statement alert('Hello') to first retrieve the object named "rect" and then to modify it:

<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<script>
<![CDATA[
function Here(){
var R=document.getElementById("Rect")
R.setAttribute("fill", "red")
}
]]>
</script>
<text id="Text" x="87" y="100" font-size="26"
fill="black">Click</text>
<rect id="Rect" onclick="Here()" x="75" y="76" height="30"
width="80" stroke="black" stroke-width="2" fill="green"
opacity=".5" rx="10"/>
</svg>
Changing the color of a rectangle

Comments:
  • each tag within an SVG document can be thought of as a "node." This is because we often think of the document as having a tree structure and computer scientists like to call the leaves of trees nodes.
  • getElementById() is the method of retrieving a particular node and dealing with it.
  • In this case, we define a new variable, R, to refer to the rectangle named 'Rect.' Think of R as an alias for 'Rect'.
  • when the function Here() is activated, the node R will have its fill attribute value changed to "red". It turns out that the simpler statement
  •  R.setAttribute("fill", "red")
  • works for SVG, though in documents using multiple namespaces, we might need the more complex appearing R.setAttributeNS(null, "fill", "red") . Those curious about the issue can follow this thread on svg-developers.

Next we'll show a more general approach that would work for any item in the document for which we wished to change the color of its fill.
...beginning same as before...
function Here(evt){
var R=evt.target
R.setAttribute("fill", "red")
}
]]>
</script>

<text id="Text" x="67" y="100" font-size="26"
fill="black">Click</text>
<rect onclick="Here(evt)" x="55" y="76" height="30"
width="80" stroke="black" stroke-width="2" fill="blue"
opacity=".5" rx="10"/>

<g transform="translate(100,0)">
<text id="Text" x="87" y="100" font-size="26"
fill="black">Click</text>
<rect onclick="Here(evt)" x="75" y="76" height="30"
width="80" stroke="black" stroke-width="2" fill="green"
opacity=".5" rx="10"/>
</g>
Changing the color of whatever <rect> is clicked on

Comments:
  • each of the two buttons, when clicked, sends a small gift (known as a 'parameter') to the function Here(). This parameter is a special thing in SVG (or HTML), evt: namely the "event."

  • evt.target is a way of referring to the thing that received the event: namely evt.target, in this case, refers to which ever rectangle was clicked on.
  • once we know what was clicked on, we let R refer to that and then change attributes of R the same way we did in the previous example.

Observe also that an object does not need to have the particular attribute beforehand to have that attribute added and then changed.
function Here(evt){
var R=evt.target
if (evt.type=="mouseover"){
R.setAttribute("stroke-dasharray", "4,8")
R.setAttribute("stroke-width", "10")
R.setAttribute("fill", "green")
}
else if (evt.type=="mouseout"){
R.setAttribute("stroke-dasharray", null)
R.setAttribute("stroke-width", "2")
R.setAttribute("fill", "blue")
}
}
]]>
</script>

<text id="Text" x="67" y="100" font-size="26"
fill="black">Click</text>
<rect onmouseover="Here(evt)" onmouseout="Here(evt)"
x="55" y="76" height="30" width="80" stroke="black"
stroke-width="2" fill="blue" opacity=".5" rx="10"/>
Rollover effect adding and removing stroke-dasharray attribute.

Comments:
  • The rectangle has both an onmouseover and an onmouseout defined. Both lead to the same function: Here().
  • We use the type of the event to determine which block of code (inside curly braces {} ) to perform
  • We adjust several attributes at the same time: "fill", "stroke-width" and "stroke-dasharray"
  • When the mouse moves out of the rectangle, we restore attributes to their original values.
  • The <rect> did not need to have its stroke-dasharray defined for its value to be set..
  • We could easily add another block of code, using another "else ... if" to trigger a function when the mouse is clicked.

Next we will confirm your suspicion that much of this could be done just using the <set> attribute of SMIL animation.
<text id="Text" x="67" y="100" font-size="26"
fill="black">Click</text>
<rect x="55" y="76" height="30" width="80" stroke="black"
stroke-width="2" fill="blue" opacity=".5" rx="10">
<set attributeName="fill" begin="mouseover" to="green"/>
<set attributeName="stroke-width" begin="mouseover" to="10"/>
<set attributeName="stroke-dasharray" begin="mouseover" to="4,8"/>
<set attributeName="fill" begin="mouseout" to="blue"/>
<set attributeName="stroke-width" begin="mouseout" to="2"/>
<set attributeName="stroke-dasharray" begin="mouseout" to="0"/>
</rect>
Rollover effect using declarative technique
Comments:
  • This example works fine in Opera and Firefox, but seems to have the following problems: IE/ASV doesn't animate the stroke-dasharray properly, and Chrome and Safari do not properly handle the rollover sometimes appearing to be confused about whether the mouse has left or entered.
  • I was just guessing when I used to="0" to remove the stroke-dasharray onmouseout. The word "null" doesn't work and the null string "" doesn't work! The issue of how many attributes there are in the DOM after such a process seems to be a matter of disagreement between the behavior of Opera and Firefox as per this example.
  • While this approach runs rather afoul of the popular approach of attempting to separate content, presentation and behavior (into respectively: markup, styles and script), there is something nice about having all aspects of an object there with it! It is also not clear, in a graphical language like SVG, what the distinction between the three necessarily is.
  • The cross-browser issues associated with the SMIL approach, regardless of its elegance, might recommend the scripted approach for such a thing. My own sense is that the bugs associated with <set> are not being fixed as quickly as those with <animate> since there has been less content developed as yet to explore such issues.
While on the topic of animation, here is a way to animate things using JavaScript. I prefer the SMIL approach, but if your audience needs to reach browsers that don't support some of the more advanced uses of <animate>, then the following approach may be of use to you..
// [ key parts of the script ]
if (evt.type=="mouseover"){
R.setAttribute("stroke", "green")
R.setAttribute("fill", "green")
running=true
animate()
}
var w=2
var dir=1
function animate(){
if (!running) return
w=w+dir
R.setAttribute("stroke-width", w)
if (w>5||w<1) dir=-dir
setTimeout("animate()", 50)
}
Comments:
  • If the scripting looks too complicated, please don't worry! It is not crucial that you understand as it is a bit of programming that may look complicated.
  • On mouseover, we call a function "animate()" that repeatedly changes the stroke-width.
  • The function animate() uses a JavaScript trick, setTimeout, to repeatedly restart itself and redraw the screen every 50 milliseconds.
  • The size of the number w increases ever 50 milliseconds until it reaches 5 and then shrinks.
  • A similar effect could be done rather easily using <animate> and would probably be understandable to more prospective web authors.
  • I am not claiming that this is good interface design -- indeed, it might be a bit annoying!.

Finally, we'll change the text inside a text node. This is something that people often want to do at about this stage of their learning, but as we can see, the conceptual approach is somewhat different since the words inside a text node are, in fact, not attributes of the text node but rather the contents of its "child" node. We'll learn more about children and parent nodes in a later lesson.
function Here(evt){
var R=evt.target
var T=document.getElementById("Text")
if (evt.type=="mouseover"){
R.setAttribute("fill", "green")
T.textContent="please"
T.setAttribute("font-size","20")
}
else if (evt.type=="mouseout"){
R.setAttribute("fill", "blue")
T.textContent="Click"
T.setAttribute("font-size","26")
}
}
]]></script>
<text id="Text" x="67" y="100" font-size="26"
fill="black">Click</text>
<rect onmouseover="Here(evt)" onmouseout="Here(evt)"
x="55" y="76" height="30" width="80" stroke="black"
stroke-width="2" fill="blue" fill-opacity=".5" rx="10"/>
Comments:
  • As before, we change the color of the <rect>. But this time we also change the font-size of the <text> named  "Text." These two things are done the same techniques described in the earlier examples, above.
  • The content of the <text> node is inside a node considered to be a child of the <text>. As such, the value of that node is not an attrbute, so we can set its textContent. Since the node inside is, in fact, the "first" child, we could instead change the content of that firstChild by T.firstChild.nodeValue="please."
  • T.textContent does not work in ASV/IE. If support for that browser matters for you then, T.firstChild.nodeValue="please" works in all the browsers.

Next steps

On to Adding New Content to an SVG Document