193

I want to know when an image has finished loading. Is there a way to do it with a callback?

If not, is there a way to do it at all?

mikemaccana
  • 110,530
  • 99
  • 389
  • 494

12 Answers12

219

.complete + callback

This is a standards compliant method without extra dependencies, and waits no longer than necessary:

var img = document.querySelector('img')

function loaded() {
  alert('loaded')
}

if (img.complete) {
  loaded()
} else {
  img.addEventListener('load', loaded)
  img.addEventListener('error', function() {
      alert('error')
  })
}

Source: http://www.html5rocks.com/en/tutorials/es6/promises/

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • 4
    Can this go wrong if the image completes between the `img.complete` call and the `addEventListener` call? – Thomas Ahle Jan 09 '17 at 18:40
  • @ThomasAhle I'm not an expert, but that seems possible. Let's see if anyone has a solution. – Ciro Santilli OurBigBook.com Jan 10 '17 at 20:39
  • I guess it would only be an issue for parallel code, which most javascript probably isn't.. – Thomas Ahle Jan 10 '17 at 21:20
  • 6
    @ThomasAhle It can't, because the browser will only fire the load event when the event queue is spun. That said, the `load` event listener *must* be in an `else` clause, as `img.complete` can become true before the `load` event is fired, hence if it weren't you could potentially have `loaded` called twice (note this is relatively likely to change such that `img.complete` only becomes true when the event fires). – gsnedders Apr 07 '17 at 16:06
  • Where is the `load` event specified? It seems I didn't find it on MDN? –  Dec 03 '22 at 22:47
  • Something not right here, if complete is false, then my event listener are never called. Src attribute is valid. – jjxtra Feb 04 '23 at 05:15
  • @user1663023, MDN seems to be lacking on the `load` event, but the HTML spec lists it at https://html.spec.whatwg.org/multipage/indices.html#event-load – Peter Feb 22 '23 at 21:48
89

Image.onload() will often work.

To use it, you'll need to be sure to bind the event handler before you set the src attribute.

Related Links:

Example Usage:

    window.onload = function () {

        var logo = document.getElementById('sologo');

        logo.onload = function () {
            alert ("The image has loaded!");  
        };

        setTimeout(function(){
            logo.src = 'https://edmullen.net/test/rc.jpg';         
        }, 5000);
    };
 <html>
    <head>
    <title>Image onload()</title>
    </head>
    <body>

    <img src="#" alt="This image is going to load" id="sologo"/>

    <script type="text/javascript">

    </script>
    </body>
    </html>
dota2pro
  • 7,220
  • 7
  • 44
  • 79
keparo
  • 33,450
  • 13
  • 60
  • 66
  • 30
    FYI: According ot the W3C spec, onload is not a valid event for IMG elements. Obviously browsers do support it, but if you care about the spec and are not 100% sure all of the browsers you want to target support this, you may want to rethink it or at least keep it in mind. – Jason Bunting Nov 12 '08 at 15:35
  • Exactly right. Be sure to test, especially if you care about supporting some of the obscure browsers. – keparo Nov 14 '08 at 09:35
  • 5
    why does waiting 5 seconds to set the source seem like a bad idea? – quemeful Aug 21 '14 at 11:42
  • 10
    @quemeful that's why it's called "an example". – Diego Jancic Nov 05 '15 at 17:45
  • 1
    Why do you need to wait to set the `src`? – Devin Gleason Lambert Jun 01 '16 at 21:04
  • 3
    @JasonBunting What are you looking at that makes you think the `load` event isn't valid? https://html.spec.whatwg.org/multipage/webappapis.html#handler-onload is the definition of the `onload` event handler. – gsnedders Apr 07 '17 at 16:28
  • 6
    @gsnedders - you realize, I'd assume, that when I wrote that comment, I was referring to the extant standard, not the one that you pointed to, which is 4.5 years newer. Would that you had asked back then, I could have pointed to it, perhaps. Such is the nature of the web, right? Things get old or are moved or are modified, etc. – Jason Bunting May 24 '17 at 20:35
  • FYI, according to the current W3C spec, `onload` _is_ a **valid** event for `img` elements, along with all HTML elements other than `body` and `frameset` elements. – Константин Ван Jul 24 '19 at 00:25
  • @JasonBunting And that’s why we need someone to point it out in the future. Otherwise the obsolete info might be thought that it still holds true. – Константин Ван Jul 24 '19 at 00:29
  • @КонстантинВан - If a developer is reading information published in a book, on the internet, etc. the onus is on _them_ to determine if the data is up to date or not. You can't place blame on an author who doesn't know the future, but placing responsibility on those who _do_, however, have access to the past, makes more sense. Also, there's nothing wrong with being backwards compatible. :) – Jason Bunting Jul 25 '19 at 17:22
  • These are all good concerns. I originally answered this in late 2008, so it has been over a decade. It is true that the spec has changed since that time, but because the specification brought official support to the load event for images, the example remains relevant. If it had gone the other way, I would change the answer accordingly. The link to the MDN reference is still good, too, and shows the browser compatibility. – keparo Aug 09 '19 at 17:10
  • Best answer. Could be leaner and clearer. Suggestions: (1) Either remove the **5-second delay** or explain it. Was it merely to demonstrate the deferred action of the event handler? Or is it required to make sure the handler gets called? (2) The HTML could be reduced to one line. (3) `alert()` may be a poor way to indicate the handler has happened. Sometimes when I run it (Chrome, Windows), the alert pops up and a blank image is below it. I guess rendering happens after the event handler returns. Makes the answer seem broken. Perhaps fill a textarea instead. TL;DR: set .onload then .src. – Bob Stein Nov 11 '19 at 03:37
  • is there any way to get loaded image content? – Sunil Garg Nov 25 '19 at 14:40
21

You can use the .complete property of the Javascript image class.

I have an application where I store a number of Image objects in an array, that will be dynamically added to the screen, and as they're loading I write updates to another div on the page. Here's a code snippet:

var gAllImages = [];

function makeThumbDivs(thumbnailsBegin, thumbnailsEnd)
{
    gAllImages = [];

    for (var i = thumbnailsBegin; i < thumbnailsEnd; i++) 
    {
        var theImage = new Image();
        theImage.src = "thumbs/" + getFilename(globals.gAllPageGUIDs[i]);
        gAllImages.push(theImage);

        setTimeout('checkForAllImagesLoaded()', 5);
        window.status="Creating thumbnail "+(i+1)+" of " + thumbnailsEnd;

        // make a new div containing that image
        makeASingleThumbDiv(globals.gAllPageGUIDs[i]);
    }
}

function checkForAllImagesLoaded()
{
    for (var i = 0; i < gAllImages.length; i++) {
        if (!gAllImages[i].complete) {
            var percentage = i * 100.0 / (gAllImages.length);
            percentage = percentage.toFixed(0).toString() + ' %';

            userMessagesController.setMessage("loading... " + percentage);
            setTimeout('checkForAllImagesLoaded()', 20);
            return;
        }
    }

    userMessagesController.setMessage(globals.defaultTitle);
}
Jon DellOro
  • 533
  • 5
  • 9
  • I've used similar code in my work before. This works well in all browsers. – Gabriel Hurley Aug 08 '09 at 21:27
  • 2
    The problem with checking complete is that starting with IE9 the OnLoad events is fired before all images are loaded and its hard to find a trigger for the checking function now. – Gene Vincent Mar 20 '13 at 10:05
19

Life is too short for jquery.

function waitForImageToLoad(imageElement){
  return new Promise(resolve=>{imageElement.onload = resolve})
}

var myImage = document.getElementById('myImage');
var newImageSrc = "https://pmchollywoodlife.files.wordpress.com/2011/12/justin-bieber-bio-photo1.jpg?w=620"

myImage.src = newImageSrc;
waitForImageToLoad(myImage).then(()=>{
  // Image have loaded.
  console.log('Loaded lol')
});
<img id="myImage" src="">
izzulmakin
  • 559
  • 1
  • 6
  • 17
Idan Beker
  • 353
  • 2
  • 9
  • 4
    This is a great answer but I don't think you even need that much code. You're just making it look confusing by adding the `src` with JS. – Brandon Benefield Mar 31 '18 at 22:25
  • 2
    Time is a huge constraint in programming . From my exprience , writing readable (while somtime long) code gives a huge time benefit. – Idan Beker Apr 01 '18 at 10:08
  • 2
    Why would you store the src into a new variable only to consume it on the next line? If brevity is your goal, that's an anti-pattern. `myImage.src = https://pmchollywoodlife.files.wordpress.com/2011/12/justin-bieber-bio-photo1.jpg?w=620` does the trick. – Josiah Dec 11 '18 at 00:03
12

You could use the load()-event in jQuery but it won't always fire if the image is loaded from the browser cache. This plugin https://github.com/peol/jquery.imgloaded/raw/master/ahpi.imgload.js can be used to remedy that problem.

jimmystormig
  • 10,672
  • 1
  • 29
  • 28
5

Here is jQuery equivalent:

var $img = $('img');

if ($img.length > 0 && !$img.get(0).complete) {
   $img.on('load', triggerAction);
}

function triggerAction() {
   alert('img has been loaded');
}
Alexander Drobyshevsky
  • 3,907
  • 2
  • 20
  • 17
5

If the goal is to style the img after browser has rendered image, you should:

const img = new Image();
img.src = 'path/to/img.jpg';

img.decode().then(() => {
/* set styles */
/* add img to DOM */ 
});

because the browser first loads the compressed version of image, then decodes it, finally paints it. since there is no event for paint you should run your logic after browser has decoded the img tag.

Sajad
  • 3,376
  • 3
  • 20
  • 23
4

Not suitable for 2008 when the question was asked, but these days this works well for me:

async function newImageSrc(src) {
  // Get a reference to the image in whatever way suits.
  let image = document.getElementById('image-id');

  // Update the source.
  image.src = src;

  // Wait for it to load.
  await new Promise((resolve) => { image.onload = resolve; });

  // Done!
  console.log('image loaded! do something...');
}
Can Rau
  • 3,130
  • 1
  • 23
  • 33
Hugh
  • 1,171
  • 1
  • 11
  • 15
0

these functions will solve the problem, you need to implement the DrawThumbnails function and have a global variable to store the images. I love to get this to work with a class object that has the ThumbnailImageArray as a member variable, but am struggling!

called as in addThumbnailImages(10);

var ThumbnailImageArray = [];

function addThumbnailImages(MaxNumberOfImages)
{
    var imgs = [];

    for (var i=1; i<MaxNumberOfImages; i++)
    {
        imgs.push(i+".jpeg");
    }

    preloadimages(imgs).done(function (images){
            var c=0;

            for(var i=0; i<images.length; i++)
            {
                if(images[i].width >0) 
                {
                    if(c != i)
                        images[c] = images[i];
                    c++;
                }
            }

            images.length = c;

            DrawThumbnails();
        });
}



function preloadimages(arr)
{
    var loadedimages=0
    var postaction=function(){}
    var arr=(typeof arr!="object")? [arr] : arr

    function imageloadpost()
    {
        loadedimages++;
        if (loadedimages==arr.length)
        {
            postaction(ThumbnailImageArray); //call postaction and pass in newimages array as parameter
        }
    };

    for (var i=0; i<arr.length; i++)
    {
        ThumbnailImageArray[i]=new Image();
        ThumbnailImageArray[i].src=arr[i];
        ThumbnailImageArray[i].onload=function(){ imageloadpost();};
        ThumbnailImageArray[i].onerror=function(){ imageloadpost();};
    }
    //return blank object with done() method    
    //remember user defined callback functions to be called when images load
    return  { done:function(f){ postaction=f || postaction } };
}
Eonasdan
  • 7,563
  • 8
  • 55
  • 82
dave
  • 49
  • 1
0

This worked for me:

// Usage
let img = await image_on_load(parent_element_to_put_img, image_url);
img.hidden = true;

// Functions
async function image_on_load(parent, url) {
    let img = element(parent, 'img');
    img.src = url;
    await new Promise((resolve, reject) => {
        element_on(img, 'load', () => {
            resolve();
        });
        element_on(img, 'error', () => {
            reject();
        });
    });
    return img;
}
function element(parent, tag_name, text = '') {
    let result = document.createElement(tag_name);
    element_child_append(parent, result);
    element_html_inner(result, text);
    return result;
}
function element_child_append(parent, result) {
    parent.appendChild(result);
}
function element_html_inner(result, text) {
    result.innerHTML = text;
}
function element_on(e, event, on_event) {
    e.addEventListener(event, async () => {
        await on_event();
    });
}

Jesus is Lord
  • 14,971
  • 11
  • 66
  • 97
0

Sorry, I can`t resist to simplify Ciro Santilli's answer from 2014.
This is code from a real-world webpage:

if (img.complete)  loaded();
else  img.onload = loaded();
j.j.
  • 1,914
  • 15
  • 12
-3

If you are using React.js, you could do this:

render() {

// ...

<img 
onLoad={() => this.onImgLoad({ item })}
onError={() => this.onImgLoad({ item })}

src={item.src} key={item.key}
ref={item.key} />

// ... }

Where:

  • - onLoad (...) now will called with something like this: { src: "https://......png", key:"1" } you can use this as "key" to know which images is loaded correctly and which not.
  • - onError(...) it is the same but for errors.
  • - the object "item" is something like this { key:"..", src:".."} you can use to store the images' URL and key in order to use in a list of images.
  • Mike
    • 335
    • 1
    • 8