Introduction

[This is an updated version of a post from Aug. 19, 2010]

Mouseover effects are a simple way to add interactivity to an SVG. There are several ways to make an SVG interactive. The most common are to use CSS and JS, but there are a couple of pure SVG solutions too. You can find the files for all these examples by clicking the Code on Github link.

Here's a static SVG bar chart made with <rect> elements and the relevant parts of the code.

<style>
  .bar {
    fill: #a9a9a9;
    opacity: 0.6;
  }
</style>
<rect class="bar" x="20" y="60" width="80" height="120"/>
<rect class="bar" x="110" y="10" width="80" height="170"/>
<rect class="bar" x="200" y="160" width="80" height="20"/>
Cats Rats Bats

It doesn't do anything at the moment, so let's add some mouseover effects.

CSS

The simplest way to add a mouseover effect is to use the :hover pseudo-selector in CSS, as you would with an HTML element. CSS styles can be put in a separate document or inside a <style> tag inside the SVG itself.

The code below changes the fill and opacity of shapes of the bar class on mouseover.

.bar:hover {
    fill: #ec008c;
    opacity: 1;
}
Cats Rats Bats

The limitations of this approach are:

  • We can only affect the properties of the element we have moused-over (but we can get around this to some extent by using groups, as explained below).
  • We can only affect 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.

Using groups

The hover effect is triggered when an element is moused-over, so what if we want to highlight a bar when we mouse-over its label?

First wrap each bar and its corresponding label with a group element, like this:

<g class="series">  
  <rect class="bar" x="20" y="60" width="80" height="120"/>
  <text class="label" x="60" y="197">Cats</text>
</g>

Then add a style that select hovering over the group and targets the bar:

.series:hover .bar {
    fill: #ec008c;
    opacity: 1;
}
Cats Rats Bats

Cursor effects

It looks a bit strange that when you mouseover the label that the cursor is a text selection cursor. You can change that with

.label {
    cursor: default;
}
Cats Rats Bats

If, for some reason, you wanted the bars labels on top of the bars (which is more likely if you're drawing a map), then you'll get an annoying effect, where the labels will block the mouse hover effect on the bars.

Cats Rats Bats

You can avoid this by grouping each label with its bar, but an easier way is to make text invisible to mouseover effects.

.label {
    pointer-events: none;
}
Cats Rats Bats

The <set> tag

EDIT: the set element is now deprecated.

Using groups lets us trigger an effect when mousing over a different element. But what if we wanted a graph where mousing over one bar, fades out the others? In that case we wouldn't be able to put all the elements in the same group as we don't want to affect the target element.

Here we could use the set tag. This element goes inside the target element and allows us to set an attribute in response to an event. The event doesn't even have to involve the target element, but instead relies on using element ids.

For example, this shows how we could change a target bar's colour while fading out the other two elements.

<rect id="rect1" class="bar" x="20" y="60" width="80" height="120">
    <set attributeName="fill" to="#ec008c"
         begin="rect1.mouseover"
         end="rect1.mouseout"/>
    <set attributeName="opacity" to="0.4"
         begin="rect2.mouseover"
         end="rect2.mouseout"/>
    <set attributeName="opacity" to="0.4"
         begin="rect3.mouseover"
         end="rect3.mouseout"/>
</rect>
Cats Rats Bats

The advantages of this method are:

  • You can change in response to events from any element without needing groups.
  • You can change any attribute, not just styles, so could even change the shape of a path.
  • You can add timing to the events, e.g. begin="4s" will cause the change to happen 4 seconds after the event is triggered.

Having said all that, if you want to add that sort of animation, then you're better off using Javascript. You can see from the code above that there's a lot of it for just three bars. If you wanted to add another bar or change the effect then you'd have to make a lot of changes.

Javascript

I've saved Javascript for last, even though it's probably the one you'll use most often. It's by far the most flexible method, allowing you to change any style, attribute or even create or delete element. The other tutorials in this section cover it in a lot more detail.

To use Javascript inside an SVG, we add an <script> element containing a CDATA marker to indicate that the contents should not be parsed as XML. I go into more detail about this, and how to using Javascript from an external file here.

<script><![CDATA[

  ]]></script>

Inside the <script> element, we can then get all the elements with a class of "bar" and loop over them, adding event listeners for the mouseover and mouseout events. This will mean that whenever the mouse is moved over or off a bar it calls the passed in functions (which we have yet defined) - in this case mouseOverEffect and mouseOutEffect respectively.

var bars = document.getElementsByClassName('bar');

for (var i = 0; i < bars.length; i++) {
    bars[i].addEventListener('mouseover', mouseOverEffect);
    bars[i].addEventListener('mouseout', mouseOutEffect);
}

Then we need to define the functions. For this example, I added a class "bar-highlight" in the mouseOverEffect function and removed it in the mouseOutEffect function.

function mouseOverEffect() {
  this.classList.add("bar-highlight");
}

function mouseOutEffect() {
  this.classList.remove("bar-highlight");
}

Finally, I added a style for the "bar-highlight" class inside the style element.

.bar-highlight {
    fill: #ec008c;
    opacity: 1;
}
Cats Rats Bats

As you can see, this approach requires a bit more work, but then it has the potential to do a lot more.

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). For example, this star was created in Inkscape and has a lot of extra attributes, but the set method works. The other methods should work just as well.

image/svg+xml