Peanuts
Peanuts

Reputation: 2681

How to preload CSS images?

I have a hidden contact form which is deployed clicking on a button. Its fields are set as CSS background images, and they always appears a bit later than the div that have been toggled.

I was using this snippet in the <head> section, but with no luck (after I cleared the cache):

<script>
$(document).ready(function() {
        pic = new Image();
        pic2 = new Image();
        pic3 = new Image();
        pic.src="<?php bloginfo('template_directory'); ?>/images/inputs/input1.png";
        pic2.src="<?php bloginfo('template_directory'); ?>/images/inputs/input2.png";
        pic3.src="<?php bloginfo('template_directory'); ?>/images/inputs/input3.png";
});
</script>

I'm using jQuery as my library, and it would be cool if I could use it as well for solving this issue.

Upvotes: 142

Views: 181879

Answers (13)

mertyildiran
mertyildiran

Reputation: 6613

Preloading images using HTML <link> Tag

I believe most of the visitors of this question are looking for the answer of "How can I preload an image before the page's render starts?" and the best solution for this problem is using <link> tag because <link> tag is capable to block the further rendering of the page. See preemptive

These two value options of rel (relationship between the current document and the linked document) attribute are most relevant with the issue:

  • prefetch: load the given resource while page is rendering
  • preload: load the given resource before page rendering starts

So if you want to load a resource (in this case it's an image) before the rendering process of <body> tag starts, use:

<link rel="preload" as="image" href="IMAGE_URL">

and if you want to load a resource while <body> is rendering but you are planning to use it later on dynamically and don't wanna bother the user with loading time, use:

<link rel="prefetch" href="RESOURCE_URL">

Upvotes: 64

vsync
vsync

Reputation: 130065

Preloading images using CSS only

In the below code I am randomly choosing the body element, since it is one of the only elements guaranteed to exist on the page.

For the "trick" to work, we shall use the content property which comfortably allows setting multiple URLs to be loaded, but as shown, the ::after pseudo element is kept hidden so the images won't be rendered:

body::after{
   position:absolute; width:0; height:0; overflow:hidden; z-index:-1; // hide images
   content:url(img1.png) url(img2.png) url(img3.gif) url(img4.jpg);   // load images
}

enter image description here

Demo


it's better to use a sprite image to reduce http requests...(if there are many relatively small sized images) and make sure the images are hosted where HTTP2 is used.

Upvotes: 323

fdrv
fdrv

Reputation: 880

I also would like add small notice.

Answer "Preloading images using CSS only" perfectly works if you do not disable the cache in dev console settings at the network tab. Otherwise it will double load the images.

enter image description here

Upvotes: 3

Peanuts
Peanuts

Reputation: 2681

I can confirm that my original code seems to work. I was casually sticking to an image with a wrong path.

Here's a test : http://paragraphe.org/slidetoggletest/test.html

<script>
    var pic = new Image();
    var pic2 = new Image();
    var pic3 = new Image();
    pic.src="images/inputs/input1.png";
    pic2.src="images/inputs/input2.png";
    pic3.src="images/inputs/input3.png";
</script>

Upvotes: 38

ezze
ezze

Reputation: 4110

When there is no way to modify CSS code and preload images with CSS rules for :before or :after pseudo elements another approach with JavaScript code traversing CSS rules of loaded stylesheets can be used. In order to make it working scripts should be included after stylesheets in HTML, for example, before closing body tag or just after stylesheets.

getUrls() {
    const urlRegExp = /url\(('|")?([^'"()]+)('|")\)?/;

    let urls = [];
    for (let i = 0; i < document.styleSheets.length; i++) {
        let cssRules = document.styleSheets[i].cssRules;
        for (let j = 0; j < cssRules.length; j++) {
            let cssRule = cssRules[j];
            if (!cssRule.selectorText) {
                continue;
            }

            for (let k = 0; k < cssRule.style.length; k++) {
                let property = cssRule.style[k],
                    urlMatch = cssRule.style[property].match(urlRegExp);
                if (urlMatch !== null) {
                    urls.push(urlMatch[2]);
                }
            }
        }
    }
    return urls;
}

preloadImages() {
    return new Promise(resolve => {
        let urls = getUrls(),
            loadedCount = 0;

        const onImageLoad = () => {
            loadedCount++;
            if (urls.length === loadedCount) {
                resolve();
            }
        };

        for (var i = 0; i < urls.length; i++) {
            let image = new Image();
            image.src = urls[i];
            image.onload = onImageLoad;
        }
    });
}

document.addEventListener('DOMContentLoaded', () => {
    preloadImages().then(() => {
        // CSS images are loaded here
    });
});

Upvotes: 1

mheager
mheager

Reputation: 11

The only way is to Base64 encode the image and place it inside the HTML code so that it doesn't need to contact the server to download the image.

This will encode an image from url so you can copy the image file code and insert it in your page like so...

body {
  background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIA...);
}

Upvotes: 1

Deminetix
Deminetix

Reputation: 2974

For preloading background images set with CSS, the most efficient answer i came up with was a modified version of some code I found that did not work:

$(':hidden').each(function() {
  var backgroundImage = $(this).css("background-image");
  if (backgroundImage != 'none') {
    tempImage = new Image();
    tempImage.src = backgroundImage;
  }
});

The massive benefit of this is that you don't need to update it when you bring in new background images in the future, it will find the new ones and preload them!

Upvotes: 7

user1236048
user1236048

Reputation: 5602

You could use this jQuery plugin waitForImage or you could put you images into an hidden div or (width:0 and height:0) and use onload event on images.

If you only have like 2-3 images you can bind events and trigger them in a chain so after every image you can do some code.

Upvotes: 1

Fatih Hayrioğlu
Fatih Hayrioğlu

Reputation: 3496

http://css-tricks.com/snippets/css/css-only-image-preloading/

Technique #1

Load the image on the element's regular state, only shift it away with background position. Then move the background position to display it on hover.

#grass { background: url(images/grass.png) no-repeat -9999px -9999px; }
#grass:hover { background-position: bottom left; }

Technique #2

If the element in question already has a background-image applied and you need to change that image, the above won't work. Typically you would go for a sprite here (a combined background image) and just shift the background position. But if that isn't possible, try this. Apply the background image to another page element that is already in use, but doesn't have a background image.

#random-unsuspecting-element { background: url(images/grass.png) no-repeat -9999px -9999px; }
#grass:hover { background: url(images/grass.png) no-repeat; }

Upvotes: 15

ajhit406
ajhit406

Reputation: 1405

If you're reusing these bg images anywhere else on your site for form inputs, you probably want to use an image sprite. That way you can centrally manage your images (instead of having pic1, pic2, pic3, etc...).

Sprites are generally faster for the client, since they are only requesting one (albeit slightly larger) file from the server instead of multiple files. See SO article for more benefits:

CSS image sprites

Then again, this might not be helpful at all if you're just using these for one form and you really only want to load them if the user requests the contact form...might make sense though.

http://www.alistapart.com/articles/sprites

Upvotes: 5

Santi
Santi

Reputation: 4468

how about loading that background image somewhere hidden. That way it will be loaded when the page is opened and wont take any time once the form is created using ajax:

body {
background: #ffffff url('img_tree.png') no-repeat -100px -100px;
}

Upvotes: 1

mck89
mck89

Reputation: 19231

try with this:

var c=new Image("Path to the background image");
c.onload=function(){
   //render the form
}

With this code you preload the background image and render the form when it's loaded

Upvotes: 10

c_harm
c_harm

Reputation:

If the page elements and their background images are already in the DOM (i.e. you are not creating/changing them dynamically), then their background images will already be loaded. At that point, you may want to look at compression methods :)

Upvotes: 0

Related Questions