The definitive best way to preload images using JavaScript/jQuery?
Asked Answered
G

8

50

I'm fully aware that this question has been asked and answered everywhere, both on SO and off. However, every time there seems to be a different answer, e.g. this, this and that.

I don't care whether it's using jQuery or not - what's important is that it works, and is cross-browser.]

So, what is the best way to preload images?

Grodno answered 23/5, 2009 at 15:2 Comment(1)
Preloading images is sooo 90s. Sprites are the in thing now. alistapart.com/articles/spritesDianoia
A
67

Unfortunately, that depends on your purpose. If you plan to use the images for purposes of style, your best bet is to use sprites. http://www.alistapart.com/articles/sprites2

However, if you plan to use the images in <img> tags, then you'll want to pre-load them with

function preload(sources)
{
  var images = [];
  for (i = 0, length = sources.length; i < length; ++i) {
    images[i] = new Image();
    images[i].src = sources[i];
  }
}

(modified source taken from What is the best way to preload multiple images in JavaScript?)

using new Image() does not involve the expense of using DOM methods but a new request for the image specified will be added to the queue. As the image is, at this point, not actually added to the page, there is no re-rendering involved. I would recommend, however, adding this to the end of your page (as all of your scripts should be, when possible) to prevent it from holding up more critical elements.

Edit: Edited to reflect comment quite correctly pointing out that separate Image objects are required to work properly. Thanks, and my bad for not checking it more closely.

Edit2: edited to make the reusability more obvious

Edit 3 (3 years later):

Due to changes in how browsers handle non-visible images (display:none or, as in this answer, never appended to the document) a new approach to pre-loading is preferred.

You can use an Ajax request to force early retrieval of images. Using jQuery, for example:

jQuery.get(source);

Or in the context of our previous example, you could do:

function preload(sources)
{
  jQuery.each(sources, function(i,source) { jQuery.get(source); });
}

Note that this doesn't apply to the case of sprites which are fine as-is. This is just for things like photo galleries or sliders/carousels with images where the images aren't loading because they are not visible initially.

Also note that this method does not work for IE (ajax is normally not used to retrieve image data).

Anastase answered 23/5, 2009 at 15:12 Comment(12)
+1 for writing everything I was about to submit. Beat me by 25 seconds. :-)Jaime
thanks for the +1 and glad I didn't try to make the grammar perfect before posting :)Anastase
Guess you didn't actually read the answer to the question you linked to. That method only preloads the last item in the array.Montemayor
What's the point of storing images in array here? IIRC, simply assigning url as a src of an Image should work just fine (and would result in lesser memory consumption).Burgess
kangax, the code as given is easily put into a routine for easy re-use. writing each one separately is much less reusable. see my editAnastase
Well, I wasn't talking about putting it in a routine. I'm curious why you store Image objects in array.Burgess
@Burgess I'm a bit late seeing your response, but the reason for the array of images is: A) if you re-use the same Image object then every time the src property is changed, the current one will be dropped (even if the download is incomplete), and B) if you create a new Image object but assign it to the same variable then you are just hoping the garbage collection doesn't spoil things for those now de-referenced Image objects. Arrays are cheap security against A and B.Anastase
I'm getting a CORS error with your latest method: XMLHttpRequest cannot load http://myfilesite.com/img.png. Origin http://mysite.com is not allowed by Access-Control-Allow-Origin.Juniejunieta
You cannot use jQuery.get for images on another server because of CORS.Dougald
Here is a fix for IE $.get() method: css-tricks.com/snippets/jquery/…Duckling
Are these images in the DOM? Causing memory usage, or are they simply sat in the cache waiting to be pulled out?Moonstone
Is it possible to perform a multipart request for with this pre-load aproach? I have about 100 images and don´t want to do 100 request to preload all the images.Valise
N
14

Spriting

As others have mentioned, spriting works quite well for a variety of reasons, however, it's not as good as its made out to be.

  • On the upside, you end up making only one HTTP request for your images. YMMV though.
  • On the down side you are loading everything in one HTTP request. Since most current browsers are limited to 2 concurrent connections the image request can block other requests. Hence YMMV and something like your menu background might not render for a bit.
  • Multiple images share the same color palette so there is some saving but this is not always the case and even so it's negligible.
  • Compression is improved because there is more shared data between images.

Dealing with irregular shapes is tricky though. Combining all new images into the new one is another annoyance.

Low jack approach using <img> tags

If you are looking for the most definitive solution then you should go with the low-jack approach which I still prefer. Create <img> links to the images at the end of your document and set the width and height to 1x1 pixel and additionally put them in a hidden div. If they are at the end of the page, they will be loaded after other content.

Nominee answered 23/5, 2009 at 23:19 Comment(2)
I'd never thought of the 'low-jack approach'... nice idea, and so simple!Hamal
It's not "pretty", but the low jack approach is the most consistent way to get it work across browsers and devices. One point of note, on firefox specifically, it works better to do visibility: hidden rather than display: none for the wrapper div and just deal with the spacing however you need to.Bluebeard
H
6

As of January 2013 none of the methods described here worked for me, so here's what did instead, tested and working with Chrome 25 and Firefox 18. Uses jQuery and this plugin to work around the load event quirks:

function preload(sources, callback) {
    if(sources.length) {
        var preloaderDiv = $('<div style="display: none;"></div>').prependTo(document.body);

        $.each(sources, function(i,source) {
            $("<img/>").attr("src", source).appendTo(preloaderDiv);

            if(i == (sources.length-1)) {
                $(preloaderDiv).imagesLoaded(function() {
                    $(this).remove();
                    if(callback) callback();
                });
            }
        });
    } else {
        if(callback) callback();
    }
}

Usage:

preload(['/img/a.png', '/img/b.png', '/img/c.png'], function() { 
    console.log("done"); 
});

Note that you'll get mixed results if the cache is disabled, which it is by default on Chrome when the developer tools are open, so keep that in mind.

Horowitz answered 26/1, 2013 at 15:10 Comment(1)
For those afraid of adding a new plugin - I was pleasantly surprised to see it readily available via npm. I put the above solution into place given 5 minutes, it's very simple. The other solutions weren't working for me either (even in chrome).Excurved
U
3

In my opinion, using Multipart XMLHttpRequest introduced by some libraries will be a preferred solution in the following years. However IE < v8, still don't support data:uri (even IE8 has limited support, allowing up to 32kb). Here is an implementation of parallel image preloading - http://code.google.com/p/core-framework/wiki/ImagePreloading , it's bundled in framework but still worth taking a look.

Upcountry answered 30/4, 2011 at 7:23 Comment(0)
D
1

This was from a long time ago so I dont know how many people are still interested in preloading an image.

My solution was even more simple.

I just used CSS.

#hidden_preload {
    height: 1px;
    left: -20000px;
    position: absolute;
    top: -20000px;
    width: 1px;
}
Dealer answered 12/4, 2015 at 20:45 Comment(0)
M
0

Here goes my simple solution with a fade in on the image after it is loaded.

    function preloadImage(_imgUrl, _container){
        var image = new Image();
            image.src = _imgUrl;
            image.onload = function(){
                $(_container).fadeTo(500, 1);
            };
        }
Memoirs answered 29/1, 2013 at 2:38 Comment(0)
O
0

For my use case I had a carousel with full screen images that I wanted to preload. However since the images display in order, and could take a few seconds each to load, it's important that I load them in order, sequentially.

For this I used the async library's waterfall() method (https://github.com/caolan/async#waterfall)

        // Preload all images in the carousel in order.
        image_preload_array = [];
        $('div.carousel-image').each(function(){
            var url = $(this).data('image-url');
            image_preload_array.push(function(callback) {
                var $img = $('<img/>')
                $img.load(function() {
                    callback(null);
                })[0].src = url;
            });
        });
        async.waterfall(image_preload_array);

This works by creating an array of functions, each function is passed the parameter callback() which it needs to execute in order to call the next function in the array. The first parameter of callback() is an error message, which will exit the sequence if a non-null value is provided, so we pass null each time.

Olivia answered 23/9, 2014 at 2:56 Comment(0)
M
-2

See this:

http://www.mattfarina.com/2007/02/01/preloading_images_with_jquery

Related question on SO:

jquery hidden preload

Mechanical answered 23/5, 2009 at 15:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.