Bottom line
For content-ful images, you want to have the src
in the HTML markup. You want to use the Javascript solution and put the rollover image in an attribute.
For content-less images UI elements, especially ones that are common across the site or duplicated, a straight CSS solution would be the best (so you don't have to re-declare the image locations at each invocation). Among the CSS solutions, sprites are the best since they don't require preloading overhead.
The Javascript solution
HTML:
<img src="/img/one.jpg" data-rollover="/img/two.jpg" />
In jQuery:
$(function(){
$('img.rollover').hover(function(){
var e = $(this);
e.data('originalSrc', e.attr('src'));
e.attr('src', e.attr('data-rollover'));
}, function(){
var e = $(this);
e.attr('src', e.data('originalSrc'));
}); /* a preloader could easily go here too */
});
Sample implementation: http://jsfiddle.net/dtPRM/1/
Benefits: It's easy; it makes sense; it works with minimal additional markup once you have your library set up.
Downsides: Requires Javascript and overhead of loading the jQuery library.
Probably the best option. If your user is using a browser where rollovers are relevant (probably the case), they have the Javascript capabilities to run this option. The folks who have intentionally turned Javascript off for some reason will clue in if you leave a little <noscript>
note saying that they may not get the full featureset.
The CSS solution: Best
HTML:
<div id="img1" />
CSS:
div#img1 {
height: 400px;
width: 300px;
background: url('http://dummyimage.com/600x400/000/fff') no-repeat top left;}
div#img1:hover {
background-position: top right;}
Sample implementation: http://jsfiddle.net/dtPRM/5/
Personally, I think that for content-ful images, this is an even worse option than the CSS + two background images solution. You're separating the HTML markup from the semantic value of the display.
If you're using content-less images like UI elements, though, this is the best solution in my opinion.
The CSS solution: Also okay
Another CSS option is available that doesn't involve background images (preferred among the CSS solutions if you want to have the image tags in the HTML, like for semantically meaningful images).
<div class="rollover">
<img class="rollover" src="http://dummyimage.com/600x400/000/fff" />
<img class="" src="http://dummyimage.com/600x400/fff/000" />
</div>
CSS (I use the :not
pseudo-selector here, but it's pretty easy to avoid using it; I also think I got the classnames semantically backwards):
div.rollover img:not(.rollover) {display: none;}
div.rollover:hover img:not(.rollover) {display: inline;}
div.rollover:hover img.rollover {display: none;}
Sample implementation: http://jsfiddle.net/dtPRM/2/
Benefits: Semantically sensible compared to the previous CSS solution of putting all the information the stylesheet.
Downsides: Extraneous markup needed.
Comment: This one may automatically pre-load depending on whether the browser calls for it.
Bottom line: A decent fallback if option (1) is unavailable because you absolutely need IE2 compatibility or non-JS support.
The CSS unsolution: Stay away
I mention this only because you mentioned it in the question. I wouldn't use it.
HTML:
<div id="img1" />
CSS:
div#img1 {
height: 400px;
width: 600px;
background: url('http://dummyimage.com/600x400/000/fff') no-repeat top left;}
div#img1:hover {
background-image: url('http://dummyimage.com/600x400/fff/000');}
Sample implementation: http://jsfiddle.net/dtPRM/4/
Benefits: Widely compatible; all you really need to support is background images and hover.
Downsides: Semantically weird to put images in CSS and to centralize it there. Makes future modifications more difficult. That being said, if you have a scenario that warrants a rollover image, there's a good chance it may be a non-content image (e.g., a UI element), in which case CSS would be semantically (perhaps) even more suitable than a regular image. See the note on sprites below.
Other downsides: You'd have to be careful to declare image height and width (a good practice anyway, but it may get cumbersome when you just want to get things done). Users viewing on mobile browsers that may treat CSS background images unusually.
Even more downsides: If you want to layer a preloader on top of it, you're going to be using Javascript and somehow selecting the rollover-able elements, and at that rate, you may as well use Javascript for everything.
Bottom line: Don't use this for content-ful images. If you must stay away from Javascript, use sprites for UI elements and the alternate solution for semantically meaningful images.