17

I was told it is necessary to set the onload function before setting src for an image object. I've searched in SO for this.

I found this code:

var img = new Image();
img.src = 'image.jpg';
img.onload = function () {
    document.body.appendChild(img);
};

But most people believe that onload should be written before src like this:

var img = new Image();
img.onload = function () {
    document.body.appendChild(img);
};
img.src = 'image.jpg';

MUST it be written in this order? Are there any cases when the above code will cause an error (like if an image is too big)?

If you anyone can show me some examples, I will be very appreciate.

Community
  • 1
  • 1
pktangyue
  • 8,326
  • 9
  • 48
  • 71

5 Answers5

9

It doesn't have to, but if setting the src and the image loads before your handler is attached, it won't fire.

JavaScript operates asynchronously. Setting the src will cause the web browser to load the image outside the main execution flow. If onload isn't set at the time that operation completes - which could be between setting src and onload.

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
  • Could the load event happen during script execution? Note the code sample does not return between setting the handler and the source. – John Dvorak Feb 01 '13 at 14:39
  • I can't understand clear, could you explain it more detailed? – pktangyue Feb 01 '13 at 14:41
  • Is it that JavaScript runs async? Or that it sometimes runs unexpectedly sync? As @JanDvorak suggests, if you're talking async, the expectation would be that the onload would run on the next frame, not in the middle of the executing script. I honestly do not know, so I'm asking, but it *seems* like the onload might be fired sync by some browsers if the image is already loaded, hence the need to define it first. – numbers1311407 Feb 01 '13 at 15:06
  • E.g. [this fiddle](http://jsfiddle.net/cP5qy/) (obviously not how `Image.src=` is defined, but illustrates what I mean), even though the `onload` is fired with an immediate timeout, it still fires when defined after. – numbers1311407 Feb 01 '13 at 15:07
  • what do you think about this question? http://stackoverflow.com/questions/7552629/can-onload-event-fire-right-after-setting-image-src-attribute – pktangyue Feb 01 '13 at 15:27
5

As soon as you assign the src a value, the image will load. If it loads before the onload is reached, your onload will not fire.

To support ALL implementations, I strongly suggest to assign the onload handler before setting the src.

It is my experience (21+ years of JS) that you MUST set onload first - especially in IE which did not even support the image object when I started with JS.

If you get issues about the cached image not firing, add +"?"+new Date().getTime() when you set the src next time to avoid cache.

Here is the example from MDN which also uses the order I have suggested

Creating an image from scratch

Another SO link image.onload not firing twice in IE7

mplungjan
  • 169,008
  • 28
  • 173
  • 236
  • what do you think about this question? http://stackoverflow.com/questions/7552629/can-onload-event-fire-right-after-setting-image-src-attribute – pktangyue Feb 01 '13 at 15:27
  • I my many years experience with JS (more years than the image object existed) the onload MUST be before the src is set. In all browsers I have used it works. In MANY browsers I have used it does not work the other way around and it is not until today in Chrome I saw it working in your fiddle – mplungjan Feb 01 '13 at 15:33
  • I believe this way can avoid many issues, though the other way works in some case. So I would accept you post. – pktangyue Feb 01 '13 at 15:42
  • 1
    I think the answer is that although implementations differ between browsers, you **must** set the `onload` first if you want it to behave consistently. – numbers1311407 Feb 01 '13 at 15:43
2

The browser will start downloading the image asychronously as soon as you assign a src, so there is the possibility the download could complete before you attach the onload event handler and never fire the code to add the image to the DOM.

Hippocrates
  • 2,510
  • 1
  • 19
  • 35
2

Most browser fire the load event imediatly if the image if the image is cached. However, Internet explorer 7 won't fire it at all. That's why it's better to set the src first.

Jean-Georges
  • 658
  • 4
  • 12
  • Not in my experience at all! - this one backs me up http://stackoverflow.com/questions/1038327/image-onload-not-firing-twice-in-ie7 – mplungjan Feb 01 '13 at 14:57
  • It says exactly what i'm saying ... "t's caused because IE will cache the image, and the onload event will never fire after it's already been loaded." – Jean-Georges Feb 01 '13 at 16:16
  • It does not follow that it is better to set src first. If you need it to run again, you just set the src to something + "?"+new Date().getTime() or similar – mplungjan Feb 01 '13 at 17:14
1

Answers above always mentioned the same problem like:

there is the possibility the download could complete before you attach the onload event handler

or

but if setting the src and the image loads before your handler is attached, it won't fire.

or bug with IE7.

First, let's ignore IE7.

Second, I don't think problem mentioned exist, for example:

function loadImg(url) {
  let img = new Image()
  img.src = url
  let date1 = Date.now()
  console.log(img.complete)
  while (Date.now() - date1 < 5000) {

  }
  img.onload = function() {
    console.log('success')
  }
  console.log('sync first')
}
loadImg('https://cdn.sstatic.net/Sites/stackoverflow/img/sprites.svg')

Normally, you will get:

false
sync first
success

Well, if you executed on another site which will use cache more than one time. You will get the result like image below:

enter image description here

The answer is clear now.

Well, as long as you set the onload synchronously. You will not miss the onload event.

Why would I say synchronously. For another example,

function loadImg(url) {
  let img = new Image()
  img.src = url
  let date1 = Date.now()
  console.log(img.complete)
  while (Date.now() - date1 < 5000) {
  
   }
  setTimeout(function() {
    img.onload = function() {
      console.log('success')
    }
  })

  console.log('sync first')
}
loadImg('https://cdn.sstatic.net/Sites/stackoverflow/img/sprites.svg')

The result on another site:

enter image description here

The second time with cache, load event won't be triggered. And the reason is setTimeout is asynchronously.

xianshenglu
  • 4,943
  • 3
  • 17
  • 34
  • So that code could also fail? `img.src = url; await new Promise(function (res) { img.onload = res; });` – fweth Mar 07 '23 at 17:08