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"/>
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;
}
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;
}
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;
}
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.
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;
}
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>
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;
}
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.
Comments (43)
Jer on 3 Dec 2010, 10:58 a.m.
Awesome! I wish all tutorials were are clear as this one. Thanks for taking the time to write it.
Peter on 3 Dec 2010, 11:36 a.m.
Thanks, I'm glad you liked it.
Ed on 5 Dec 2010, 10:26 p.m.
Hi, thanks for your page. I'm using inkscape to create an svg file and trying to understand some of these interactive functions. I am viewing my files in opera portable I have used : onmouseover="evt.target.setAttribute('opacity', '0.5');" and the associated mousout function and it works a treat. I then tried to change different attributes and couldn't get any others to work. Are there any particular syntax rules that I need to. If I want to change say the stroke width what should I write. I tried : onmouseover="evt.target.setAttribute('stroke-width', '5');" Thank you so much if you can give me any pointers ! Ed
Peter on 5 Dec 2010, 11:18 p.m.
Hi Ed,
I had a quick test using Opera Portable and the onmouseover events seemed to work. The only thing I can think of is that the default stroke is for there to be no stroke, so if you're going to altered the stroke width, you need to define a colour.
For example, see if this works:
<rect id="rect1" x="160" y="10" width="60" height="60" fill="blue" stroke="black"
onmouseover="evt.target.setAttribute('stroke-width', '5');"
onmouseout="evt.target.setAttribute('stroke-width', '1');"/>
Hope that works,
Peter
Ed on 6 Dec 2010, 12:06 a.m.
Hi Peter,
I tried that and on your file and it works just great. I will keep playing around with my inskscape model and see if I can get the same logic to work. Many thanks,
Ed
Peter on 6 Dec 2010, 1:25 a.m.
I guess something is overriding the onmouseover effect. If you are using CSS to style the stroke then that will take precedence. I'm not really sure how to get around that other than by not using CSS (which is a bit of shame).
Will on 26 Oct 2011, 3:29 a.m.
Hi and thanks for the great Tutorials... But I've got still one Question.
I haved used Inkscape to draw a star in SVG, but can't get the onmouseover effect working. The Code that won't work look like this:
<path
sodipodi:type="star"
style="fill:#aaaaaa;fill-opacity:0.9...
id="element123"
sodipodi:sides="6"
sodipodi:cx="241.42857"
sodipodi:cy="203.79076"...
onmouseover="evt.target.setAttribute('sodipodi:cx', '246');"
onmouseout="evt.target.setAttribute('sodipodi:cx', '241.42857');"
inkscape:label="#bg-element123" />
perhaps somone know the correct value to move elements with a path-attribute?
thx Will
Will on 26 Oct 2011, 10:17 p.m.
thanks for nothing.....
Jespa on 2 Nov 2011, 1:41 p.m.
Really great tutorial. Thanks!
Fönster on 25 Nov 2011, 10:36 p.m.
Hi
please show us how to do this to incscape svg s
Thanks!
Peter on 18 Dec 2011, 12:37 p.m.
Hi Will and Fönster, sorry I didn't see your comments earlier. The code works the same when using Inkscape SVGs, you just need to make sure you're changing the right attributes.
For example, changing sodipodi:cx, has no effect because it's only interpreted by Inkscape and not most other SVG viewers such as a browser. You can verify this by manually changing the value in your SVG; you won't see any effect when you open the SVG in a browser.
Since you will mainly be dealing with paths, it's easiest to use the transform attribute.
For example, the following will cause a shape to jump up 10 pixels when you mouseover it. I've attached a full example to the bottom of the post (above the comments).
onmouseover="evt.target.setAttribute('transform', 'translate(0, 0)');" onmouseout="evt.target.setAttribute('transform', 'translate(0, 10)');"
SasQ on 2 Jul 2012, 12:33 a.m.
How to make it work with <use> elements? I could change only the opacity attribute. Nothing else changes :-/
And, better still, how to make it work with CSS rules? I don't want to re-declare all those attributes again in the script, I have them already in my CSS styleshit, so the only thing I need is to apply these rules to the element (eg. stroke width or color change etc.).
SasQ on 13 Nov 2012, 5:10 a.m.
May be I am late finding this post ( this post 2010 ).
I am open your star attachment in my browser.. its work, cool....
and I see your inserted code in my Inkscape.
Eric on 2 Jan 2013, 9:50 p.m.
Hello. Thanks again for your site.
I am trying to make an svg image of a US map interactive so when you click a state the state flag appears. Could you advise on how to make images appear/disappear with mouse clicks?
Madha on 21 Jan 2013, 7:07 a.m.
Hi, thanks for the useful tips. I don't know why it is not working for me. I am asked to use Java Scripts to write my own function for mouseover and mouse out. Still, it is not working. This is what I have written:
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="300">
<script type="text/javascript">
<![CDATA[
function FillColor1(evt) {
evt.target.setAttributeNS(null,"fill","red");
}
function FillColor2(evt) {
evt.target.setAttributeNS(null,"fill","green");
}
]]>
</script>
<circle id="face" cx="150" cy="150" r="100" stroke="black" stroke-width="2" fill="yellow"></circle>
<ellipse id="eyeL" cx="120" cy="100" rx="15" ry="25" style="fill:blue;stroke:black;stroke-width:2" onmouseover="Fillcolor1(evt)" onmouseout="FillColor2(evt)"></ellipse>
<ellipse id="eyeR" cx="180" cy="100" rx="15" ry="25" style="fill:blue;stroke:black;stroke-width:2" onmouseover="Fillcolor1(evt)" onmouseout="FillColor2(evt)"></ellipse>
<polygon id="mouth" points="105,180 130,200 170,200 195,180 165,190 135,190" style="fill:blue;stroke:black;stroke-width:2" onmouseover="Fillcolor1(evt)" onmouseout="FillColor2(evt)"></polygon>
<rect id="nose" x="140" y="145" width="20" height="20" style="fill:blue;stroke-width:2;stroke:black" onmouseover="Fillcolor1(evt)" onmouseout="FillColor2(evt)"></rect>
</svg>
thanks,
Mahda
Peter on 22 Jan 2013, 6:02 p.m.
Hi Mahda,
There's a couple of things with your code. First, you're calling Fillcolor1(), with a lowercase 'c'. Second, you have styled your shapes using the style attribute which overrides any changes you've made. Change you shapes to be styled like this:
<ellipse id="eyeL" cx="120" cy="100" rx="15" ry="25" fill="blue" stroke="black" stroke-width="2" onmouseover="FillColor1(evt)" onmouseout="FillColor2(evt)"></ellipse>
Jayant Parbat on 27 Jan 2013, 6:02 p.m.
Im making an intereactive map, im implimenting your pop effect code, it works but the svg image moves to the top of the page.
P.S great tutorials.
Cindy on 24 Apr 2013, 7:11 a.m.
Hi,
Sorry, I am late to the page also. Just a quick question regarding funtionality of the hover effect. Is it possible to have a hover effect that highlights a common word/concept within the text on a page?
I would really appreciate your help with this.
Ta
Cindy
Ashok Koparday on 15 Jul 2013, 9:51 a.m.
Hi Peter,
Awesome work.
How does this compare with the result generated using CSS3 animation?
Best wishes,
Ashok Koparday
Jeff Darling on 2 Apr 2014, 12:54 a.m.
I find myself coming back to this page frequently. I have a project that I'm working on that uses an interactive SVG. I used a google search for SVG display image on mouseover.
I used my own class like this
class="foo-bar"
<style>
.foo-bar:hover
{
opacity: 0.5;
}
</style>
avantika on 15 Apr 2014, 3:26 p.m.
Very useful. These tricks helped me reduce many lines of code. Thank you!
Patrick on 21 Nov 2014, 3:08 p.m.
Thanks for this tutorial, it saved me a great deal of time.
Patrick,
Nairobi.
Nina on 3 Dec 2014, 7:30 p.m.
I am really grateful for this .... it has helped me a lot.
Sunny on 7 Jan 2015, 5:59 p.m.
Just wanna say this is an amazing blog...I love all the efforts you made here...It's very easy to learn and follw. And the explainations of Chinese are funny. Looking forward to seeing more.
Dan on 26 Mar 2015, 12:38 p.m.
Thanks for this, the tip for using pointer-events: none; solved the problem I was having with hovering over text.
Zainab on 27 May 2015, 9:41 a.m.
I am trying to make an svg image of a India map interactive so when you
click a state,popup with information regarding the state appears. Could you advise on how to makepop ups appear/disappear with mouse clicks?
Rahul Nambier on 15 Jun 2015, 8:01 a.m.
Hey its a very nice post, it helped me a lot
Anonymous on 6 Apr 2016, 11:17 p.m.
Hi Peter,
Thanks so much for your tutorials--they are excellent. I'm working on a school project with an SVG in which I want to display an image (a detailed map in my case) when I mouseover a country.
I see in your code that something like `onmousemove="ShowTooltip(evt, 'Box')"` will display the word `Box` when you hover over an item. How can I put an image in the place of the text `Box`?
Thanks again for your help!
Andre
Marijose on 7 Apr 2016, 5:21 p.m.
Great job Peter! Just what I was looking for. Thank you!
Fahim Akhtar on 25 Jun 2016, 5:19 p.m.
Simple and to the point.
Markus on 9 Sep 2016, 2:35 p.m.
Hi there. Thanx for you post, helped me a lot.
Regards
Markus
Ari Indiarto on 21 Jan 2017, 1:18 p.m.
Thank you.. This blog answering my head's questions. I'm new in SVG.
Awesome Article..
Fiziwater on 16 Mar 2017, 1:34 a.m.
Very well organized and easy to follow tutorials! Practical techniques I can use in my work. Thank you!
Anonymous on 23 Jul 2017, 7:32 p.m.
Hi,
The tutorial was very clear. But I need to display an SVG icon#1 upon SVG icon#2 when howering on SVG icon#2. Please tell how can we do it.
Hosting on 16 Sep 2017, 4:43 a.m.
In this example i couldn t understand why not have four separated paths in one SVG and just have them rotate, but then i tried it out and you can t reference inside the use element
Darrell Myers on 6 Oct 2017, 6:58 p.m.
Thanks, your site is a great help (I'm a newbie)! One thing I keep having trouble with is distinguishing where SVG ends and where Javascript (or something else) begins. I want to start by learning what SVG can do before branching into Javascript if necessary. For instance, is your example "Continuous events on mouse hold" using "pure" SVG? The fact that it has functions in it implies Javascript to me, but that's not mentioned anywhere on this page. If you're still monitoring this site, could you tell me if it is indeed all SVG? And in any case, do you know of any sites or other references for SVG that are super clear about distinguishing it from other markup languages or code? You'd think such a thing would be easy to find, but I'm having trouble doing it.
Peter Nierop on 12 Oct 2017, 11:30 p.m.
Outstanding stuff. I recently took to SVG and had much fun working through your code. Most of it seems still relevant and your style of education is a delight.
Alexand on 7 Mar 2018, 6:26 a.m.
Just want to let you know, 8 years later and this is still helping out.
Appriciate it!
Peter on 4 May 2018, 11:45 p.m.
Thanks Alexand - 8 years later and I've finally updated the page!
Venkata on 2 Oct 2018, 2:49 p.m.
Awesome. Thanks for posting such a great tutorial.
Peter Kandra on 3 May 2019, 8:14 p.m.
I have some SVG code on a jade template page and I need to add inline style for a hover, but I can't seem to get it working. Below is my svg code: Any help would be appreciated.
svg(version='1.1',x='0px', y='0px', viewBox='0 0 32 32',enable-background='new 0 0 32 32')
g(id="Layer_6")
path(style='fill:none;stroke:#414246;stroke-width:1.80642104;stroke-linecap:round;stroke-linejoin:round'
d='M5.7537679,23.160935 H 13.767948')
path(style='fill:none;stroke:#414246;stroke-width:1.80642104;stroke-linecap:round;stroke-linejoin:round'
d='M5.7537679,19.272205 H 18.877838')
path(style='fill:none;stroke:#414246;stroke-width:1.80642104;stroke-linecap:round;stroke-linejoin:round'
d='M5.7537679,15.372305 H 26.261828')
path(style='fill:none;stroke:#414246;stroke-width:1.80642116;stroke-linecap:round;stroke-linejoin:round'
d='M1.2055513,10.064397 H 11.87742 V 1.2923912')
path(style='fill:none;stroke:#414246;stroke-width:1.80642116;stroke-linecap:round;stroke-linejoin:round'
d='M1.0000596,31.184259 H 31.193639 V 0.9906789 H 10.822562 L 1.0000596,9.6621142 Z')
Ciprian on 30 Jan 2020, 3:15 p.m.
I was wondering if i can use the '<set attributename' to modify an svg in react?
SPR on 7 Aug 2020, 8:41 a.m.
Hi
i successfully animated my svg image and wanted to use it as a background image for the body of a website.
It is Displayed successfully but there is no hover effect. I used css animation.
Do you know a way to fix this?