changing html images on hover/a quick css trick
Changing an image on hover in css is a piece of cake. It's a common technique that has been used for a long time and received a number of improvements over the years (think css sprites). Changing an html image on hover is a little harder and when I ran into this very problem a week or so ago it had me stumped for about 30 seconds. More than enough to dedicate an article on a selection of solutions to refresh people's memory.
what we want to accomplish
Hovers on html images are not too common, as most images in html are purely content images and are not related to the actual design. One important exception is the site logo, which is best placed in html instead of css. It's common practice to put a home link on the site logo and when hovered, it's nice to see a hover state indication, subtle as it might be. To summarize what it is we want to do exactly:
- Place an image in the html source
- Change the look of the image when hovered
- Stay clear of any javascript and solve the problem using css only
- The image has to show up on print at all times, as seen on the page.
The final condition is the reason why we can't rely on css images only, and even though sprites can be used, we have to make sure only the base image shows up on print.
the first solution: sprites
a {display:block; width:100px; height:50px; overflow:hidden;}
a:hover img {margin-left:-100px;}
/* ie6 needs his fix*/
a:hover {zoom:1;}
Our first solution makes use of sprites. This means that both versions of the image are created in the same file. In this case, the image is 100px wide, so the hover state of the image is placed
100px to the right of the base image. We give the link the dimensions of the image and add overflow:hidden to hide the hover state. When
we hover, we simply pull the image to the left and up comes the hover state. IE6 is not very cooperative and needs a little fix, a simple
zoom:1 does the trick though.
A pretty easy trick, which shows up well on print too (as the overflow:hidden is applied), only when disabling the css altogether does this technique fail, as both states of the image will show up next to each other. Apart from that, the technique has good browser support with only IE6 being a little annoying (what else is new).
the second solution: separate images
a {display:block; width:100px; height:50px; background:url("logo-2.gif") left top no-repeat;}
a:hover img {position:absolute; left:-999em; top:-999em;}
/* ie6 needs his fix*/
a:hover {zoom:1;}
The second solution is a little dirtier, but shows up well even with css disabled. The idea is to make a separate image for the hover state and to place it behind the actual
image in css. When the link is hovered, we simply position the base image away and what remains is the hover state of the image defined in css. A simple trick, but effective,
with no lag as the css image is defined on the link and is loaded when the page is renderen for the first time. Again, IE6 needs a little push to make it work, adding a simple
zoom:1 is all it takes.
Apart from the little IE6 tweak this is a pretty handy solution, though it does require you to load two separate images. If you're aiming for speed, this might not be your choice of preference.
a third alternative: transparent images
a {display:block; width:100px; height:50px; background:#fff; overflow:hidden;}
a:hover {border:0px solid red; background:#00FF21;}
The third solution is the easiest to do in css but requires some graphical preparation and only works when doing color effects. Still, it's a nice technique that could use some more attention. The trick is to make part of your image transparent. You then define a base background color which you overwrite on hover. You can use gifs for simple images or pngs for more interesting masks. More information can be found in my article on png masks.
A very effective method, though hard to accomplish with png masks as ie6 has bad png support. Apart from that, also limited in effect as it can only be used to influence the color of your logo.
a final variation: css transparency
To finish off the article there's a final variation on the color theme described in the third alternative. Rather than use a transparent html image it's also possible to use css transparency to get a nice highlight effect. Here too you can do simple highlights using background colors or try more complex masks by using images. More information on this technique can be found on the SocialGeek blog.
rounding up
Maybe (read: probably) I missed some solutions, but these were the ones that sprung to mind immediately. They all have their pros and cons but between the three of them you should have more than enough options to accomplish what you want. Nothing too difficult or complex, but it gives your site that little extra.

Comments
Thomas Byttebier
Nice overview. I have gotten so used to the sprites technique that I use it nearly everywhere now. But as you point out that may not be the most stylish way of doing things.
SocialGeek
I think the sprites technique is only workable if the image is defined in the css file. Considering html images, I'm much more in favor of the latter solutions provided in this article for the sole reason of the full sprite showing up when style sheets are disabled or unavailable (as pointed out in the article).
John Faulds
I always use the 2nd solution as it's the one that will work best in most situations.
Niels Matthijs
Well, the second solution is my preferred one too, will be using it in my new blog design.
As for the first solution, I think there's an alternative way to handle it by clipping certain parts of the image in css (clip property). Ran into it the other day, have no idea how well it is supported cross-browser though.
广告笔
i am used to the first one.Maybe the bad one,but i used to it.
Dave
I've got to ask: what is the picture that illustrates this article?
Niels Matthijs
Hah!
It's actually a hovercraft (largest one in the world I believe). The craft of hovering etc ... :)
Wout de Zeeuw
Ellooo,
Tried solution with the 2 images, but unfortunately if you do that in IE 7, and you try using applying it to 2 different buttons with 2 css id's, then IE 7 will use the first hover image on the second id... argh! The sprite image approach works better. In firefox there's a little wierdness though, it does something different if you do or , isn't that wierd???
Wout
djlebarron
I couldn't come up with a simple way of putting my lettering on the "non-hovered" image in the second solution. As it's presented, I would have to have my text as part of the actual image. I also would have to have an ID for each link and provide seperate background images and seperate base images for each link in each link's unique CSS (this also relates to Ellooo's comment). Too much muck.
I simply switched the "blank" background images via CSS a:hover, and then placed my text (once) in the link. If a user comes to the site with CSS disabled, the text link still shows. Anyone going to my site with CSS disabled is going to have a hard time making heads or tails of it anyway, and I suspect that's the case with most modern sites. I'll provide for the JS disabled, but having to provide for the CSS disabled may be a bit extreme and counterproductive. Like I said, the text links will still be there for the CSS disabled.
Here's the code with five links...
Luke Day
@djlebarron,
That works great, but its better if you have two images in one and move the background image, this ensures that there is no flicker or wait when the hover state is loaded.
Brian K
Used the 2nd solution. I'm not a frontend person by trade (Ruby guy actually), but was able to use this elegant method very quickly and easily. I showed it to one of our designers and he really liked the implementation. I forwarded this post to him for a CSS talk he's giving later today. We're going to take a look at using the 2nd solution over sprites in certain areas of our site.
Thank you.
ambilibmenon
my cell of the table contains 3 (text) links to diffrent files.as i move over on the cell,my background color of that particular cell is being changed. but i want the color of all the 3 text also to change along with the background change. i am successful in changing the cell background. but not able to change all the 3 text colors at the same time. please help me out of this problem if u r willing. my email-id: ambilibmenon@yahoo.co.in
Gary
beautiful site you should be proud
elamonii
thanks a lot. its really helpful :)
sick
The second solution worked great for me. I'm running a source theme from Elegant Designs and it fits the dimensions great. I've padded each cell more than normal to give it that new millenia feel.
Mahesh
How to Change the particular color of the image in css
Bill
Very, very helpful. Thank you.
allan
The second option does what I want, however I seem to loose the href link that the first image is attached to - any ideas?
Compton
Would I be able to create these as #Name then reference the ID with in the <a id=Name Etc...
Page
Great thing is with a little bit more code you can make it animate in. CSS3 transitions are great.
-webkit-transition: opacity 1s ease-in-out; -moz-transition: opacity 1s ease-in-out; -o-transition: opacity 1s ease-in-out; transition: opacity 1s ease-in-out;
John
I agree with Page, we should try to look at using css3 and html properties... not to do it for the sake of doing it but to try out new things.
jakes
I’ve been searching around the net and haven’t really come up with an answer. So I’m asking if maybe you know if it’s possible to use “inline CSS Sprites” with my employer’s logo for an email signature. Just a simple mouse over effect from grey to color with link (possibly without use of “background”. The closest thing I found thus far is http://css-tricks.com/3427-css-sprites-with-inline-images/ but they are using embedded css. Email compatibility for browsers email and app mail like to strip embedded css.
Any help would be much appreciated thanks.
fe
thanks!
Brian K.
Thanks. This is what I was searching for and I think I'm going to use the 2. version which seems OK for me. First one is better if there is a speed problem wince the second one is working after both image loaded
czmarci
First solution is the best :) thank you.