34

I know there is a lot of these on Stackoverflow but I haven't found one that works for me in a recent version of jquery (1.10.2).

I did try:

$(".lazy").load(function (){}

But I believe after some research using .load to detect image load is deprecated in jQuery 1.8. What I need to do is fire an image resize function once the images are loaded. I don't have control over the HTML and at the moment I am having to add the image dimensions via jQuery by attaching an attribute (via .attr()) once page loads so that I can use lazyload js.

The problem is that I need an accurate way to hold off all my various scripts until the image has loaded properly else the functions sometimes fire before every image had loaded. I have tried using $(window).load(function (){}); however it sometimes still fires before every image had loaded.

Alex W
  • 37,233
  • 13
  • 109
  • 109
Gerico
  • 5,079
  • 14
  • 44
  • 85
  • Are you certain about `$(window).load()`? I've never had that issue, and from the jQuery docs: "Run a function when the page is fully loaded including graphics." – Mister Epic Dec 16 '13 at 15:04
  • you can check if the DOM is ready using $(document)ready(); – patel.milanb Dec 16 '13 at 15:04
  • I'll re-run my tests and double check i am correct about window.load – Gerico Dec 16 '13 at 15:06
  • "But i believe after some research this is depreciated in jQuery 1.8" What is supposed to be deprecated? – A. Wolff Dec 16 '13 at 15:07
  • Update question. I meant i think .load to detect image loads doesn't work since 1.8 – Gerico Dec 16 '13 at 15:11
  • It works, only syntax .load() is depreciated, should use `.on('load',handler)` instead (which shouldn't fix your issue anyway). You could still try something like that: `$('.lazy').on('load',handler).attr('src',function(){return this.src})` – A. Wolff Dec 16 '13 at 15:13
  • Have you tried $("element").on('load',function(){}); – Talha Masood Dec 16 '13 at 15:14

5 Answers5

52

I usually do this:

var image = new Image();
image.onload = function () {
   console.info("Image loaded !");
   //do something...
}
image.onerror = function () {
   console.error("Cannot load image");
   //do something else...
}
image.src = "/images/blah/foo.jpg";

Remember that the loading is asynchronous so you have to continue the script inside the onload and onerror events.

Darko Romanov
  • 2,776
  • 2
  • 31
  • 38
  • If i am loading a large list of images, i take it i need to reference all the image paths? – Gerico Dec 16 '13 at 15:25
  • Well, it depends on what you have in mind. If the process needs all images to be loaded before starting then yes, you have to load them all. – Darko Romanov Dec 16 '13 at 15:58
  • If my list was dynamic, I could make a variable which looks at the src attribute of all images coming in, then apply it that way? – Gerico Dec 17 '13 at 10:27
  • Yes, absolutely. The static array was just an example, you could read all the src before starting and then launch the preload function. – Darko Romanov Dec 17 '13 at 11:31
30

There's also a useful .complete property of an image object, you can use it if you have already set the .src of your <img> before attaching to it any event listeners:

var img=document.getElementById('myimg');
var func=function(){
    // do your code here
    // `this` refers to the img object
};
if(img.complete){ 
    func.call(img);
}
else{ 
    img.onload=func; 
}

Reference: http://www.w3schools.com/jsref/prop_img_complete.asp

optimizitor
  • 807
  • 13
  • 18
  • This was exactly what I needed to catch the end of an image loaded via a `$(xx).html()` call – Nick Sep 30 '19 at 08:46
20

I would give the images that require this constraint a class like mustLoad where:

<img class="mustLoad" src="..." alt="" />

and then create a generic image load handler function, such as:

$('img.mustLoad').on('load',function(){
        /* Fire your image resize code here */
});

Edit:

In response to your comments about deprecating .load() above, .load() was deprecated, in favor of .on('load') to reduce ambiguity between the onLoad event and Ajax loading.

Community
  • 1
  • 1
Alex W
  • 37,233
  • 13
  • 109
  • 109
14

In the case of waiting of loading multiple images:

var images = $("#div-with-images img");
var unloaded = images.length;
images.on('load', function(){
  -- unloaded;
  if (!unloaded) {
    // here all images loaded, do your stuff
  }
});
Mihause
  • 141
  • 1
  • 6
3

What I need to do is fire an image resize function once the images are loaded.

Are you sure that you need the image to be loaded? Waiting for an image to load before resizing it can cause a large jump in the page layout, especially if the images have large file sizes, such as animated GIFs.

Usually, for an image resize, you only need to know the intrinsic dimensions of the image. While there is no event to tell you this, it's easy enough to poll the images for the data. Something like this could be particularly effective:

<img src="..." data-resizeme="123" />
(function() {
    var images, l, i, tmp;
    if( document.querySelectorAll) {
        images = [].slice.call(document.querySelectorAll("img[data-resizeme]"),0);
    }
    else {
        tmp = document.getElementsByTagName("img");
        images = [];
        // browser compatibility is fun!
        for( i=tmp.length-1; i>=0; i--) {
            if( tmp[i].getAttribute("data-resizeme")) images.unshift(tmp[i]);
        }
    }

    for( i=images.length-1; i>=0; i--) {
        images[i].onload = resizeImage;
        images[i].onerror = cancelImageResize;
    }
    var timer = setInterval(function() {
        for( i=images.length-1; i>=0; i--) {
            if( images[i].width) {
                resizeImage.call(images[i]);
                images[i].onload = null;
                cancelImageResize.call(images[i]);
            }
        }
        if( images.length == 0) clearInterval(timer);
    },100); // adjust granularity as needed - lower number is more responsive.

    function cancelImageResize() {
        var i;
        for( i=images.length-1; i>=0; i--) {
            if( images[i] == this) {
                images.splice(i,1);
                break;
            }
        }
    }

    function resizeImage() {
        console.log("Image "+this.src+" is "+this.width+"x"+this.height);
    }
})();

Hope this helps!

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • Is data-resizeme="123" - specifically the 123, the size of the image that i want it sized to? – Gerico Dec 16 '13 at 15:29
  • 1
    You can make it so. In the `resizeImage` function, use `parseInt(this.getAttribute("data-resizeme"),10)` to get the value, then apply it. The point is to have an attribute there. What you use it for is up to you. – Niet the Dark Absol Dec 16 '13 at 15:49
  • using setInterval to make a polling function is very very bad, that's the worst way to manage asynchronous functions, instead use a callback system. – Darko Romanov Dec 16 '13 at 16:19
  • @DarkoRomanov Erm... how would you suggest polling for something that doesn't have an associated event handler? – Niet the Dark Absol Dec 16 '13 at 16:35
  • The event is onload of the Image DOM object, you just need an array with the url of each image and a simple pointer to the current image. When an image completes loading then you call again the same loading function, increasing your pointer. See the Fiddle http://jsfiddle.net/93Q7Y/ – Darko Romanov Dec 16 '13 at 16:51
  • @DarkoRomanov But it's not. I'm listening for the `.width` property existing, which happens as soon as the image *starts* to load (notice how the browser reserves the space once it knows the image's intrinsic dimensions). This helps reduce the layout of the page jumping around. – Niet the Dark Absol Dec 16 '13 at 17:11
  • With this i get: TypeError: images.splice is not a function images.splice(i,1); – Gerico Dec 17 '13 at 10:25
  • Whoops, forgot to convert the NodeList to an array before trying to use array functions on it! Try it now. – Niet the Dark Absol Dec 17 '13 at 11:34
  • @NiettheDarkAbsol 1) the width of the image will be known after the image is fully loaded 2) you're polling (!) the width of the image with 100ms interval, so if the width of the image was loaded after, say, 910ms you wouldn't know it after 1000ms, wasting 90ms doing absolutely nothing. I really don't understand why you should do polling when you can call back functions as soon as the images load. – Darko Romanov Dec 18 '13 at 10:30
  • @DarkoRomanov 1) Incorrect. The dimensions of the image are one of the first things in the image's data, and therefore are known almost immediately when the image starts to download, well before its load event is fired. 2) As the comment indicates, granularity may be adjusted, however a 90ms delay is insignificant compared to the 910ms in your example. **The main point here** is that an image could take MINUTES to download, but the METADATA containing the width and height will ALWAYS load near-instantly. – Niet the Dark Absol Dec 18 '13 at 10:49
  • @NiettheDarkAbsol 1) please provide a Fiddle or reference. 2) 90ms to 910ms is 10%, for me it's **HUGE** – Darko Romanov Jan 01 '14 at 00:06