Mouseover effects in SVGs

In this tutorial I'll describe five different methods to achieve a mouseover effect in an SVG. I'll start with the simplest and most limited approach (CSS), and work up to the most complex, but most flexible approach (Javascript/ECMAScript, which is described in more detail here). To view the full code for any of examples in this post, right click on an image and chose View Frame Source or something similar, depending on your browser.

For further information see:

Example SVG

SVGs seem to be an increasingly popular way of adding high quality, interactive images to the web. Even IE9 now supports SVGs to a degree. Older IE users will have to use a Chrome Frame or something similar. Note, that I've tested only these effects in Chrome and Firefox. In order to demonstrate mouseover effects, we need a simple SVG. The code below draws two squares: one blue, one green.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="300" height="80">

  <text x="10" y="35">Two passive squares:</text>

  <rect id="rect1"  x="160" y="10" 
   width="60" height="60" fill="blue"/>

  <rect id="rect2" x="230" y="10"
   width="60" height="60" fill="green"/>

</svg>

CSS

The simplest way to get a mouseover effect is to use the hover effect with CSS styling. Note that the style element can be written within the SVG itself and works just as with HTML. For example, add the following code into the <svg> element:

  <style>
    rect:hover
    {
      opacity: 0.5;
    }
  </style>

CSS within SVG works just as with HTML, so we can get more specific effects by using different selectors. For example, using .some-class-name:hover allows us to selectively apply the mouseover effect to only those elements with the attribute class="some-class-name".

If you can get away with just using CSS, I'd recommend it, but there are some limitations:

  • We can only effect the properties of the element we have moused-over (although we can partially get around this using groups and more advanced CSS selectors).
  • We can only effect the style of an element, not its other attributes such as size or position.
  • We can only trigger events with a mouse hover, and not, for example, on a mouse click.

Onmouseover events

A more targeted approach is to add a function directly into the <rect> element. The function onmouseover is called when the mouse is moved over an element, and we can set this to changes the element's opacity attribute (or any other attribute). To get the full hover effect we also need to change the opacity back to normal when the mouse leaves the <rect> element by setting the onmouseout function.

<rect id="rect1" x="160" y="10"
width="60" height="60"  fill="blue"
onmouseover="evt.target.setAttribute('opacity', '0.5');"
onmouseout="evt.target.setAttribute('opacity','1)');"/>

This method requires more code, especially if you want to add effects to multiple elements, but gives us a bit more flexibility. For example, if we could chose not to return the opacity to 1 when we move the mouse out of the box, or we could trigger effects with onmouseup or onmousedown events. We also have more freedom in which attributes we change. For example, if we write:

<rect id="rect1" x="160" y="10"
width="60" height="60"  fill="blue"
onmousedown="evt.target.setAttribute('y', '5');"
  onmouseup="evt.target.setAttribute('y', '10');"/>

Then the box will 'jump' when we click on it. Finally, we are also not limited to just effecting the element we've moused over. If we change evt.target to evt.target.parentNode.getElementById('rect2'), we select the element with id='rect2', i.e the green rectangle. This approach however, is a little indirect, and I would suggest using ECMAScript instead.

Set attributeName

We can also add a set element to a <rect> element. This allows us to set an attribute in response to an event, even if that event does not involve the element we are changing. Here I used the rect2.mouseover event to trigger an effect in rect1.

Note that this effect does not work in Firefox Firefox 6 and earlier (I think), which is perhaps the biggest drawback of this method.

  <rect id="rect1" x="160" y="10"
   width="60" height="60" fill="blue">
    <set attributeName="fill-opacity" to="0.5"
     begin="rect2.mouseover" end="rect2.mouseout"/>
  </rect>

The set element is pretty versatile and can be used for various animation effects. For example, with begin="4s", the effect will begin 4 seconds after loading.

ECMAScript

The final method I'll describe is the most involved, but by far the most flexible (for example, we can create a tooltip). It involves using ECMAScript (like JavaScript) to write our own functions. The functions are created within a <script> element. Below is one function to make an element semi-transparent and another to make an element opaque. 

  <script type="text/ecmascript">
    <![CDATA[
      function MakeTransparent(evt) {
        evt.target.setAttributeNS(null,"opacity","0.5");
      }

      function MakeOpaque(evt) {
        evt.target.setAttributeNS(null,"opacity","1");
      }
    ]]>
  </script>

Now we can set the onmouseover and onmouseout functions to equal these custom functions.

  <rect id="rect1" x="160" y="10"
  width="60" height="60" fill="blue"
  onmouseover="MakeTransparent(evt)"
  onmouseout="MakeOpaque(evt)"/>

  <rect id="rect2" x="230" y="10"
  width="60" height="60" fill="green"
  onmouseover="MakeTransparent(evt)"
  onmouseout="MakeOpaque(evt)"/>

Clearly, this method requires a lot more writing for the same effect, but since we can write our own functions, it has the potential to generate much more complex effects. I've written a more detailed description of what can be done with ECMAScript here.

JavaScript

Instead of using ECMAScript the SVG itself, it is also possible to use JavaScript in the HTML that contains the SVG, which I explain here. There are several JavaScript libraries that make this even easier. For example, AmpleSDK or Polymaps, which is specific for SVG maps.

Inkscape images

Since a couple of people have asked, you can use these effect with Inkscape SVGs. Just make sure you target the right attributes (such as 'transform' if you want to move or scale a shape). See the attached star.svg for an example.

AttachmentSize
star.svg2.36 KB