Preloading CSS Images
Asked Answered
C

13

141

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 arranging this issue.

Thanks for your thoughs.

Carvelbuilt answered 3/9, 2009 at 12:34 Comment(2)
Please, show your code. How do you toggle this form?Ashcraft
n1313, I use the simpliest way possible : $('#toggle').click(function() { $('#contact').slideToggle(); });Carvelbuilt
C
37

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>
Carvelbuilt answered 3/9, 2009 at 17:52 Comment(3)
After a few tests with CSS-ONLY and this solution, in my case specifically, this is the best one.Sequestrate
The URL in the answer is no longer valid.Triclinic
Hi, I re-uploaded the contents on that URL (paragraphe.org/slidetoggletest/test.html), sorry for having missed this. Cheers,Carvelbuilt
C
321

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.

Cochin answered 17/1, 2013 at 23:47 Comment(11)
Best technique so far for its simplicity, and pure CSS approach! The only drawback really is that these images are loaded at the same time as the immediately visible content, and not delayed until after the main content has loaded, which would require Javascript. But unless you're preloading dozens of images, this shouldn't be a deal-breaker.Fossiliferous
this is fantastic for preloading hover assets for CSS buttons and such - that way you don't get any flicker when you over over your button while the assets loadEdessa
Good solution. I would recommend not applying this to body, though, as these images will get loaded onto every page that references the stylesheet. Instead, you should use .contact_form:after {...} or some other class that is less global. Unless, of course, your contact form is on every page ;)Pfeffer
@Pfeffer - one cannot make recommendations here. Each use-case is unique. I do use it on the body myself because there are svg and gif images, what's known as "loading animations", which can show up anywhere anytime, so they must be preloaded cross-site.Cochin
@vsync, that makes sense, especially for gifs and small reusables. The situation I came across recently was for large bg images for a content rich carousel, and I certainly didn't want those large files loaded everywhere :)Pfeffer
This doesn't seem to be working on IE any workaround?Silvey
@DannyG - Updated the code. IE ignores images if the element has display:noneCochin
Minor suggestion: try adding @media print { display: none; } to prevent odd printing artifacts that sometimes creep up.Pall
@Pall - in what scenario would those artifacts appear?Cochin
This do not work in my case. I dont know why. I just pul the list of images, and then try use it for background for a different tag, but the url of the image the same. And I still see how it try to load again. So just double loadingThimerosal
Is this supposed to stop it from showing another request for those images in the network tab? mine still loads the subsequent images after the fact. does it matter if I have background: url vs content: url in my images vs preload?Vining
R
60

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 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">
Rese answered 22/1, 2017 at 7:43 Comment(5)
It's worth noting that the Mozilla browser engine will only load prefetch when the browser is idle, which is determined by the nsIWebProgressListener API. See this for more info.Tabanid
I don't think this actually blocks rendering: w3c.github.io/preload "For example, the application can use the preload keyword to initiate early, high-priority, and non-render-blocking fetch of a CSS resource that can then be applied by the application at appropriate time"Protomorphic
@MichaelCrenshaw is correct, <link rel="preload"> does not block rendering, I believe the author misunderstood the definition. From MDN Preloading content with rel="preload", "... which you want to start loading early in the page lifecycle, before browsers' main rendering machinery kicks in." The idea is that resources are fetched as soon as possible, making it more likely that the resource is available when needed, for example when an <img> element is rendering.Position
I wanted to use the image as a background-image with css and this worked for me, while the accepted answer did not.Abney
while preload worked for me, using prefetch the image gets downloaded again when the css requires itCongregationalism
C
37

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>
Carvelbuilt answered 3/9, 2009 at 17:52 Comment(3)
After a few tests with CSS-ONLY and this solution, in my case specifically, this is the best one.Sequestrate
The URL in the answer is no longer valid.Triclinic
Hi, I re-uploaded the contents on that URL (paragraphe.org/slidetoggletest/test.html), sorry for having missed this. Cheers,Carvelbuilt
F
15

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; }
Fescennine answered 6/1, 2011 at 12:50 Comment(3)
Always provide content of external links. Otherwise your answer is worthless if the link breaks!Kimbell
@Fatih +1 for very good link. More current version of the linked article with 3 different ways is here perishablepress.com/3-ways-preload-images-css-javascript-ajaxBobsled
@Bobsled There are no CSS-only solutions in your link.Electrostriction
C
10

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

Cole answered 3/9, 2009 at 12:51 Comment(2)
mck89, thanks this looks pretty good. But, should I use this code inside the 'head' or inline ? I mean, must I fill it with html ?Carvelbuilt
You must use it in the head. I think that you can use it inside the $(document).readyCole
D
7

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!

Dominican answered 19/10, 2012 at 4:7 Comment(1)
Do I understand it correctly that you do add a hidden element for each image ? It's not that it's able to scan your CSS file for images right ? :)Dumuzi
E
5

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

Ellery answered 3/9, 2009 at 21:26 Comment(1)
Yes, CSS Sprites are the way to go. Just make sure that one of the images in that sprite appears before the hidden form is launched (like on page load).Mamie
T
4

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

Thimerosal answered 17/4, 2021 at 0:2 Comment(0)
G
1

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;
}
Gunsmith answered 3/9, 2009 at 12:52 Comment(0)
A
1

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...);
}
Antiphonal answered 25/9, 2011 at 6:0 Comment(1)
if you would like to use the same image again in your CSS...it won't be cached and you'll have to load it again.Cochin
I
1

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.

Insolate answered 14/3, 2012 at 23:5 Comment(0)
N
1

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
    });
});
Novikoff answered 28/10, 2016 at 19:19 Comment(0)
J
0

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

Jaddo answered 3/9, 2009 at 12:45 Comment(2)
mmmm... good point cpharmston, and this explains why the issue persists even with the images cached. So, that does means there's no workaraound ?Carvelbuilt
Create smaller images! JPEGs are easily compressed, there are command line tools for reducing the size of PNGs (pmt.sourceforge.net/pngcrush), and you can reduce the number of colors in the image to reduce the size of GIFs. Of course, you can also spring for faster internet service ;)Tarshatarshish

© 2022 - 2024 — McMap. All rights reserved.