css: avoid image hover first time blinking

45,825

Solution 1

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');
}

Solution 2

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.

Solution 3

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;
 }

Solution 4

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 :)

Solution 5

@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(?).

Share:
45,825
Haradzieniec
Author by

Haradzieniec

Updated on July 05, 2022

Comments

  • Haradzieniec
    Haradzieniec almost 2 years

    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:

    • Should I "preload" the hovered image? How?
    • Should I play with z-index or opacity?

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

  • Haradzieniec
    Haradzieniec over 11 years
    Nice solution. Thanks for your answer.
  • Bugs Bunny
    Bugs Bunny almost 9 years
    What if I'm using background-size: contain? Any ideas?
  • Civilian
    Civilian over 8 years
    Love it! Great answer, doesn't require making sprites, you can dump all of your extra images in there.
  • Caye
    Caye over 8 years
    Awesome, simple and more intuitive, for me at least
  • Srneczek
    Srneczek over 8 years
    Kinda hacky, but it works. I would add position: absolute; or much better display: none; instead of visibility: hidden; if you don't want to mess your layout.
  • yorbro
    yorbro almost 8 years
    It's a good solution but does not work so well when you increase the number of icons in your application, as the number of images you load at the initial load increases every time.
  • Srneczek
    Srneczek almost 8 years
    if I was developing browser Id load only the valid background (so the last one) to minimize the network load. I think you can expect this is not gonna work in any browser sooner or later.
  • Srneczek
    Srneczek almost 8 years
    @yorbro I guess you can combine sprites with this approach
  • anthonygore
    anthonygore almost 8 years
    OP wants a different image for default state and hover so this wouldn't work
  • anthonygore
    anthonygore almost 8 years
    Would this work if you wanted to use transition to fade between the images? I'm thinking not..
  • anthonygore
    anthonygore almost 8 years
    Doesn't work if you actually want to use the pseudo element for something
  • Aaron Franke
    Aaron Franke over 7 years
    This wasn't working for me at first, but I realize now that for some reason the element you apply these styles to MUST be body:after!
  • MightyPork
    MightyPork over 7 years
    this worked OK until a recent chrome update, now it's optimized by the browser (which is great, but breaks the preload)
  • Kristian Svensson
    Kristian Svensson over 6 years
    Good catch! Thank you for sharing!
  • Toniq
    Toniq over 6 years
    @Aaron Franke this works for me on any element:after
  • fgonzalez
    fgonzalez over 6 years
    great solution!
  • sbnc.eu
    sbnc.eu over 4 years
    Looks like this does not work any more. My guess is that browsers optimize page loads in a way that the background for hidden elements is not loaded. Tried in recent Firefox, Safari and Chrome versions, none seem to load the image when display: none; is in the declaration. Same results when I try with width: 0; height: 0. If it is visible, it works, however adds unwanted image to the page. Anyone else facing the same?
  • ladiesman1792
    ladiesman1792 over 4 years
    @Grapestain same with me. Not working when there's display: none;
  • bilbohhh
    bilbohhh about 4 years
    Nice! I would suggest to use position:absolute; width:0; height:0; overflow:hidden; z-index:-1; though. Somehow left: -50000px freaks me out) Basically, the problem is the same as described here: stackoverflow.com/a/14390213/4810382
  • taylor michels
    taylor michels over 3 years
    this solution seems to work in modern browsers stackoverflow.com/a/14390213/1108467
  • jinyong lee
    jinyong lee about 3 years
    I wouldn't call this the easiest solution to fix flickering if the CSS is already otherwise ready. But it is a good way nonetheless.
  • josue.0
    josue.0 over 2 years
    I used only width 1 and height 1 and it worked for me
  • Dominik
    Dominik over 2 years
    Here are also some solutions. Take a look and leave some feedback. stackoverflow.com/a/70673030/9251185
  • CodeNinja
    CodeNinja about 2 years
    If you have developer tools open, make sure the "disable cache" checkbox is not selected. Banged my head against this for days before I finally realized. Using this solution worked for me!