Preload :hover images in CSS
Now that the majority of CSS blogosphere is accessing the Internet with some kind of broadband connection, we kind of forget about Dial-up users. We do all kind of trickery to make our images lighter and faster to download, but even though, sometimes the first time visitor gets the flicker when hovering a link with a background image.
Most of the time, pixy’s workaround with a single image, which is positioned accordingly will do, but sometimes for whatever reason, we need completely separate images. Preloading background images with CSS is so cheap trick, that I sometimes laugh at myself how could I forget about it.
a { background: url(image_hover.gif); }
a:link { background: url(image_default.gif); }
a:hover,
a:focus { background: url(image_hover.gif); }
Update
The code example was rather generic, now it’s altered for those who are in a hurry : ) and have no time to read through valuable discussion in the comments.

27 Comments
It’s mind-numbingly simple. Thanks for reminding me :)
Comment (#) by Rob Mientjes — 22nd June 2005.
That works? Huh. I’ve been using the sprite technique since before it showed up on ALA, but I’ll keep this in mind. Of course, I don’t have any style graphics on my own site, but hey…
Comment (#) by Clay — 22nd June 2005.
Very cool, I was just thinking about this the other day. Thanks for providing the help, that’s just what I was looking for.
Comment (#) by Justin P — 22nd June 2005.
Oh yeah! Totally forgot about that! I usually do the positioning one image thing.
Comment (#) by Crano — 23rd June 2005.
I’ve been using an extra div (I know, boo hiss) with the id “preload". I position this off stage, and ba-da-bing.
This method is much cleaner though. Thanks.
Comment (#) by Peter Flaschner — 24th June 2005.
How does this work? I am not following.
Comment (#) by Jared Christensen — 24th June 2005.
I think it works because the browser first sees the “a” selector, and so will load the hover image, since “a” covers all anchor states.
You then “undo” that with the a:link selector, which states the non-hover image.
a:hover and a:focus are there just to make sure that the browser knows to load the hover image for those states.
Comment (#) by Sage — 27th June 2005.
Hmph… nice idea. I’ve also been using the “position hover images off screen” technique described by Peter, but your suggestion is very simple and cleaner. Nobody aware of any instabilities with this method?
Thanks.
Comment (#) by Leo — 28th June 2005.
the problem with pixies rollovers is that he uses fixed width (or height) buttons, which don’t scale very good (to say the least). But why should your method be better for modem/isdn users ? you have to download all images, be it in one merged (pixie) or be it in two seperate. i would be suprised if the difference in filesize would be that much. prob. a few bytes. the real advantage of pixies method is that the rollover-state is there when you *need* it. Your method seems to do the job as well, besides it allows us to use *wallpaper*-buttons, that scale much better. so 2:1 for you ;-)
No matter which one you choose,the css will get messed up again with tricks, and workarounds.
Comment (#) by michael — 29th June 2005.
Hey, that’s pretty nifty. I’m guessing it works how Sage described it, but a confirmation would be nice!
I’ll still stick to the Pixy method for my rollovers. I tend to merge images together anyway, hopefully saving size in the process but definitely saving trips to the server and thus speeding up rendering time.
Comment (#) by trovster — 29th June 2005.
@trovster:
Consider Sage’s description confirmed : )
Comment (#) by marko — 30th June 2005.
Hi! I’m still a bit new to using CSS style sheets. I still don’t understand how to use your tip here, but it sounds like it would be the best thing to avoiding adding more html to my pages to preload the images. I want to use it for my navigation buttons. An example of some of my nav button CSS code can be found at http://www.esbuys.com on any page of the site (all using the same CSS file). How do I incorporate your tip into it? Thank you!
Comment (#) by Eric — 3rd July 2005.
Maybe I am being a bit slow today but I could not get this to work.
I think it was (either todays slowness) or the order of the links and the a:visited selector.
I tried this:
a{background: url(img_hover.gif); }a:link, a:visited { background: url(img_default.gif); }
a:hover { background: url(/img_hover.gif); }
This loads the background image for all links states, turns it back to the top image for the up state and visited links and then lastly overrides this with hover.
Comment (#) by Matt Keogh — 7th July 2005.
Well, I don’t know if it’s correct or not, but you are missing the comma after your first line:
a{background: url(img_hover.gif); },At least you’re missing it if you’re trying to use the example from Marko… also, you’re missing the a:focus part…
Comment (#) by Eric — 8th July 2005.
Eric, thanks for looking into this for me but I meant to leave that comment out. You can’t have a comma after the declaration only after the selector.
I re-checked my method looking at my temporary internet files folder and the rollover image does not appear in there until after I have rolled over (it does not get preloaded).
I have re-tried the above method and it shows the rollover image without rolling over. It is as though the a:link does nothing. I am going to look into this further.
Does anyone have a link to a working example so I can work out what I am doing wrong and confirm this works?
On a side note - the a:focus does not work for IE but a:active does.
Comment (#) by Matt Keogh — 8th July 2005.
I suppose another technique would be set the background inside a different element such as a list item and then use the background position to move this away.
No as elagant as the above method though…
Comment (#) by Matt Keogh — 8th July 2005.
argh! Just found out about the IE Image flicker problem! Is there no end to this madness!?
It appears that the bg image does load into the cache but then expires or something after a short while and the page reloads from the server rather than the cache.
great.
Comment (#) by Matt Keogh — 8th July 2005.
Great idea, saved me a load of trouble.
I think the :hover class needs to be defined last though.
This setup works for me:
a{background: url(hoverimage.gif) 30px 40% no-repeat;}a:link,a:visited{background: url(normalimage.gif) 30px 40% no-repeat;}
a:hover{background: url(hoverimage.gif) 30px 40% no-repeat;}
Comment (#) by Tim — 6th August 2005.
Ok, I’m not a huge fan of preloading images in css, but I could see it’s uses. If any of you are looking for something in Javascript I have a clean implementation up on my site http://jehiah.com/archive/simple-swap
Comment (#) by jehiah — 19th September 2005.
Wow, I just can’t get this to work. I’m using unique IDs for each of the links; could this be the problem? My code looks something like this:
#home {background: url(image-hover.jpg)}#home:link {background: url(image.jpg)}
#home:visited {background: url(image.jpg)}
#home:hover {background: url(image-hover.jpg)}
The hover image does not preload for me at all; I get a flicker while the hover image loads for the first time in every browser (though it seems like sometimes it preloads the hover image in Safari; don’t ask me why).
What am I doing wrong? (Oh and you can see the site?and flickering?in action at www.lisadejohn.com.)
Comment (#) by feaverish — 12th October 2005.
Update: I still couldn’t get this technique to work, but I was able to get the same effect by applying the hover image to the background of the container element (in my case the list item element) and the default, non-hover image to the contained link states (except :hover, obviously). The only drawback (besides a ton of IDs) is that a visitor with a slow connection will see the hover image load before the default image. In my case it wasn’t a big deal, and it actually looks kind of cool.
Zeldman details the procedure here (2nd item).
Comment (#) by feaverish — 12th October 2005.
This seems like such a cool and simple way of dealing with preloading backgrounds, and I understand how it is supposed to work - but I couldn’t get it to work in any browser I tried. Has ANYONE had success with this? And could you point out an example of it working?
Back to javascript preloading for now…
Comment (#) by Matthew — 25th October 2005.
I ran into the same problem where I was using list elements for a menu, and all menu items had its own image / hover-image pair. The solutions I’d seen I didn’t like or didn’t work so I came up with a solution I’m happy with and which seems to work always to get rid of the flicker. The solution I came up with is to simply add the hover state image within the HREF and give the image a visibility:hidden style (I’m using style="preLoad” and then have the CSS in a seperate file). I feel this is the ‘cleanest’ way to do it without too much overhead.
For an example have a look at the source code of this site.
Just my 2 cents, hope it helps.
Mike.
Comment (#) by Mike — 7th November 2005.
Just thought I’d let the people having problems know that the reason it doesn’t work as written here in IE is that IE goes with the “
a” background rather than the “a:link” background when the link is already visited. The corrected code that also works in IE is this:a { background: url(image_hover.gif); }
a:link,
a:visited { background: url(image_default.gif); }
a:hover,
a:focus { background: url(image_hover.gif); }
Comment (#) by Cuzog — 21st November 2005.
are you sure is’s working? i’ve tested under xpsp2 lastest patched with httpwatch, the hover image is NOT preloaded
Comment (#) by moo — 3rd January 2006.
Thanks a lot. I apply your solution but i have to do some changes to work in IE. I’ve use a shim image for each menu. The result is XHTML 1.0 compliant and works very well. You can check it at www.doliaku.com.ar (is the main menu).
PS: sorry for my english language, i hope you understand what i am trying to say!
Comment (#) by Lucas Recalde — 3rd January 2006.
I landed on your site looking for a way to preload background images for links. I could not get your solution to work. This is what I was doing:
a.about {background: url(images/menu/about_ON.gif) 50% 50% no-repeat;
}
a:link.about {
background: url(image/menu/about.gif) 50% 50% no-repeat;
}
a:hover.about, a:focus.about {
background: url(images/menu/about_ON.gif) 50% 50% no-repeat;
}
I found another solution which seemed to work for me. I ended up creating a div on the page with class imageLoader which looked like this:
.imageLoader {background: url(images/menu/about_ON.gif);
background: url(images/menu/products_ON.gif);
background: url(images/menu/downloads_ON.gif);
background: url(images/menu/messages_ON.gif);
background: url(images/menu/news_ON.gif);
background: url(images/menu/contact_ON.gif);
visibility: hidden;
}
then I had my link backgrounds set up normally with a, and a:hover classes.
it is working on mac (Safari and Netscape), but have not tested all browsers.
Comment (#) by Martin Bour — 19th November 2006.
Sorry, the comment form is closed at this time, but if you have anything to say, please send me a message.