Haradzieniec
Haradzieniec

Reputation: 9340

css: avoid image hover first time blinking

I have an anchor that changes its background image when hovered with a class class-btn that contains a background-image.

When hovered, it has

a.class-btn:hover
{
    background-image('path/to/image-hovered.jpg');
}

When the page loads the first time and you hover this button the first time, it blinks (it takes about half a second to download the hovered image). How to avoid that blinking without JavaScript (only simple css and html is allowed)?

I tried to search Stack Overflow for the similar question, but with no luck.

Just added:

It happens with all browsers and thus the solution should work for all browsers.

Upvotes: 73

Views: 49678

Answers (14)

John Mark
John Mark

Reputation: 65

For me this worked a.class-btn{transition-delay: 0.1s;}

Upvotes: 1

Kristian Svensson
Kristian Svensson

Reputation: 1112

Here is a simple and effective css image preloading technique I have used several times. You can load several images by placing content: url() url() url()... etc.

body:after {
 display: none;
 content: url('path/to/image-hovered.jpg') url('path/to/another-image-hovered.jpg');
}

Upvotes: 95

Jordi Parra Crespo
Jordi Parra Crespo

Reputation: 71

I had the same issue. After trying everything related with css i can not solve the problem. What finally solved the problem was simulating that someone hovers the element. The css is the normal one.

CSS

#elemName{
 /* ... */
} 
#elemName:hover{
 /* change bg image */
}

JS

var element = document.getElementById('elemName');
var event = new MouseEvent('mouseover', {
  'view': window,
  'bubbles': true,
  'cancelable': true
});
element.dispatchEvent(event);

Upvotes: 0

Colin James Firth
Colin James Firth

Reputation: 144

This technique works nicely for me and ensures not only is the hover image pre-loaded, but it's also ready and waiting to be displayed. (Most other solutions rely on switching the background image on hover, which just seems to take the browser a bit of time to figure out, however much the image is pre-loaded.)

Create :before and :after pseudo elements on the container with the two images, but hide the one you want to see on hover. Then, on hover, switch the visibility.

So long as they both share the same size and positioning rules, you should see a neat swap.

.image-container {
    &:before { display: block; background-image: url(uncovered.png); }
    &:after { display: none; background-image: url(uncovered.png); }
}
.image-container:hover {
    &:before { display: none; }
    &:after { display: block; }
}

Upvotes: 0

hdscp
hdscp

Reputation: 31

@Kristian's method of applying hidden 'content: url()' after the body didn't seem to work in Firefox 48.0 (OS X).

However, changing "display: none;" to something like:

body:after {
 position: absolute; overflow: hidden; left: -50000px;
 content: url(/path/to/picture-1.jpg) url(/path/to/picture-2.jpg);
}

... did the trick for me. Perhaps Firefox won't load hidden images, or maybe it's related to rendering(?).

Upvotes: 3

Martin Staufcik
Martin Staufcik

Reputation: 9490

This is a non-CSS solution: if the hover images are in one directory and have a common naming convention, for example contain a substring '-on.', it is possible to select the file names and put it into the HTML as a series of:

<img src='...' style='display: none' />

Upvotes: 2

Benjamin Dordoigne
Benjamin Dordoigne

Reputation: 99

If you do this:

#the-button {
background-image: url('images/img.gif');
}
#the-button:before {
  content: url('images/animated-img.gif');
  width:0;
  height:0;
  visibility:hidden;
}

#the-button:hover {
  background-image: url('images/animated-img.gif');
}

This will really help!

See here:

http://particle-in-a-box.com/blog-post/pre-load-hover-images-css-only

P.S - not my work but a solution I found :)

Upvotes: 9

Callam
Callam

Reputation: 1009

A simple trick I use is to double up the original background image making sure to put the hovered image first

.next {
  background: url(../images/next-hover.png) center center no-repeat;
  background: url(../images/next.png) center center no-repeat;
    &:hover{
      background: url(../images/next-hover.png) center center no-repeat;
    }
 }

No performance hit and very simple

Or if you're not using SCSS yet:

.next {
  background: url(../images/next-hover.png) center center no-repeat;
  background: url(../images/next.png) center center no-repeat;        
 }
 .next:hover{
  background: url(../images/next-hover.png) center center no-repeat;
 }

Upvotes: 15

Nailgun
Nailgun

Reputation: 4169

The "double up the original background image" trick didn't work for me so I used another css trick:

.next {
    background: url(../images/next.png) center center no-repeat;        
}
.next:hover {
    background: url(../images/next-hover.png) center center no-repeat;
}
.next:after {
    content: url(../images/next-hover.png);
    display: none;
}

Upvotes: 0

Spencer Lively
Spencer Lively

Reputation: 31

The best way to do this is to just insert the images onto the webpage and set display to none.

Upvotes: -3

Pluto
Pluto

Reputation: 3026

Just change the size of the background image, instead of the source of it! So...

a.class-btn {
    background-image: url('path/to/image-hovered.jpg');
    background-size: 0;
}
a.class-btn:hover {
    background-size: auto;
}

Upvotes: -2

Anshuman Jasrotia
Anshuman Jasrotia

Reputation: 3175

You can preload images

function preloadImages(srcs, imgs, callback) {
var img;
var remaining = srcs.length;
for (var i = 0; i < srcs.length; i++) {
    img = new Image();
    img.onload = function() {
        --remaining;
        if (remaining <= 0) {
            callback();
        }
    };
    img.src = srcs[i];
    imgs.push(img);
}
}
// then to call it, you would use this
var imageSrcs = ["src1", "src2", "src3", "src4"];
var images = [];
preloadImages(imageSrcs, images, myFunction);

Upvotes: 2

asc99c
asc99c

Reputation: 3905

If they are the same dimensions, one possibility is to draw the two images directly on top of each other, with the CSS :hover class for the top image having display: none;

This way both images will be preloaded, but hovering will make the second visible.

Upvotes: 0

CherryFlavourPez
CherryFlavourPez

Reputation: 7497

The easiest way to avoid this is to make use of image sprites. For a good overview, check out this CSS Tricks article.

That way, you not only solve the flicker problem you're seeing, but will also reduce the number of HTTP requests. Your CSS will look something like:

a.class-btn { background: url('path/to/image.jpg') 0 0 no-repeat; }
a.class-btn:hover { background-position: 0 -40px; }

The specifics will depend on your images. You can also make use of an online sprite generator to make the process easier.

Upvotes: 62

Related Questions