6

EDIT:I've changed the title, because the issue had nothing to do with IE image.load() firing - my substr() wasn't working (see accepted answer).


There's a ton of posts about making sure that you define your onload handler prior to assigning img.src in order to guarantee that the onload handler is in place in case the image is loaded from cache first.

This does not appear to the be issue in my code, since that's precisely what I have done.

Note that this script works across all other browsers, but IE 8 and lower does not trigger the onload inline function.

var i = lastSlideIndex || 1;

while(imagesQueued < MAX_IMAGES){
    var preloadImage = new Image();
    preloadImage.arrayIndex = i;
    preloadImage.onload = function(eventObj){
        debug('Image ' + this.src + ' loaded.');
        slideshowImages[this.arrayIndex] = this;
        if (this.arrayIndex > lastImageIndex) lastImageIndex = this.arrayIndex;
        triggerSlideshow(this.arrayIndex);
    }

    // add leading zeros
    var leadingZeros = "0000000".substr(-(String(MAX_IMAGES).length));
    imageNumber = leadingZeros.substr(String(i).length) + i;

    debug('preloading Image #' + imageNumber);
    preloadImage.src = fullImagePrefix + imageNumber + "." + IMAGES_TLA;

    if (++i > MAX_IMAGES) i = 1;
    imagesQueued++;
}

Any other suggestions would be deeply appreciated!

Tom Auger
  • 19,421
  • 22
  • 81
  • 104

3 Answers3

11

Now that this question is retitled to be about substr() in IE 8, here's the quick fix to the negative index problem. Paste the following code from Mozilla Developer Network:

// only run when the substr() function is broken
if ('ab'.substr(-1) != 'b') {
  /**
   *  Get the substring of a string
   *  @param  {integer}  start   where to start the substring
   *  @param  {integer}  length  how many characters to return
   *  @return {string}
   */
  String.prototype.substr = function(substr) {
    return function(start, length) {
      // call the original method
      return substr.call(this,
        // did we get a negative start, calculate how much it is from the beginning of the string
        // adjust the start parameter for negative value
        start < 0 ? this.length + start : start,
        length);
    }
  }(String.prototype.substr);
};

This code detects the broken implementation of substr and replaces it with a compliant one.

E Ciotti
  • 4,740
  • 1
  • 25
  • 17
josh
  • 9,038
  • 8
  • 31
  • 37
  • 1
    Wicked. Thanks Josh for picking up this old thread and posting something really useful! – Tom Auger Feb 14 '13 at 15:15
  • Another option is to just replace `substr` with `slice`. – Bart Feb 27 '13 at 21:00
  • Will it work if `this.length + start < 0`? It will result in calling substr with negative `start` argument, which seems to give wrong result. – fdermishin Nov 22 '15 at 15:01
  • @user502144 looks like MDN subsequently updated their polyfill to address that bug... just updated my answer to have their latest code. – josh Nov 23 '15 at 20:21
  • @josh But what will happen if `this.length === 5` and `start === -7`? There is only check `start < 0`, but it is not checked if `this.length + start < 0` – fdermishin Nov 24 '15 at 16:41
  • 1
    @user502144 you tell me :-) don't have ie 8 installed right now. what _should_ happen is it starts from 0 -- it should be equivalent to .substr(0, length). Not sure what ie 8 does in practice... if it's something else, we should let the MDN guys know... – josh Nov 24 '15 at 20:32
  • thanks man, you helped me to fix a gov.uk project's bug; I've exchanged the favour with a small code edit (jslint flagged a couple of missing semicolons) – E Ciotti Jun 03 '16 at 11:32
9

Update: As commenters have pointed out (and I can't prove them otherwise for now), I removed the first suggestion.

Couple of more things to note:

onload event will not fire if the image is being loaded from cache. Try clearing your cache and retry.

Another problem is that IE doesn't like negative in substr. Use slice instead:

"0000000".slice(-(String(MAX_IMAGES).length));
Mrchief
  • 75,126
  • 20
  • 142
  • 189
  • No, this is simply not true. IE definitely does support "load" events on Image object instances. If it did not, after all, your suggestion wouldn't work either. [Here](http://jsfiddle.net/e8uFa/) is the jsfiddle. – Pointy Aug 02 '11 at 21:46
  • I agree with Pointy. This is not true. IE (all versions) support the load handler for images. Another JSFiddle showing that to be true: http://jsfiddle.net/jfriend00/D59SD/. – jfriend00 Aug 02 '11 at 21:48
  • Again, you are wrong. onload will fire when the image comes from the cache as long as you have set the load handler before setting the .src property. – jfriend00 Aug 02 '11 at 22:05
  • @jfriend00 now about that, I'm not so sure. See my answer. – Pointy Aug 02 '11 at 22:17
  • @Pointy, not so sure about what? I use onload handlers in IE all the time on versions from IE6 to IE9 and they absolutely work if you do them right - whether the image is cached or not. – jfriend00 Aug 02 '11 at 22:22
  • @jfriend00 I'll do a fiddle variation - IE (maybe just IE8) doesn't call the load handler when "src" is set in the same event loop as the handler is established, but it will call it if it's either bypassing the cache or if the "src" is set in a separate event loop. – Pointy Aug 02 '11 at 22:25
  • @Mrchief: thank you for the substr hint. In fact that's what was causing the problem. Replacing with slice() did the trick and if I had only been paying attention to my debug I would have caught that it was trying to load images that didn't exist! Great catch, man. Thanks – Tom Auger Aug 04 '11 at 14:29
  • @Tom: Glad it helped! There was a recent issue with someone else. HE thought adding `class` was not working in IE but it was due to the same issue that his code was not getting called. – Mrchief Aug 04 '11 at 14:33
  • "IE doesn't like negative in substr. Use slice instead:" You just solved a head-melter for me with that. Thanks :) +1 – ConorLuddy Oct 08 '15 at 14:54
1

Two ways of dealing with this:

  1. Add a nonce parameter to your image URLs.

    var nonce = new Date().getTime();
    
    // ...
    
    preloadImage.src = fullImagePrefix + imageNumber + "." + IMAGES_TLA + ('?_=' + nonce++);
    
  2. Set the "src" property in a different event loop.

    setTimeout(function(img, src) {
      img.src = src;
    }(preloadImage, fullImagePrefix + imageNumber + "." + IMAGES_TLA), 1);
    

By using a nonce parameter each time you fetch an image, you bypass the cache. Now, that's probably not such a great idea, so the second option gets around the problem by making sure that the "src" property is set in a separate event loop. The "load" will trigger then.

Here is an example. The code uses nonces on some images but sets the "src" for all of them in a timeout handler. As you can see, they all load (turn red).

I don't know why IE doesn't fire the "load" handler when the image is in cache, but it does when the "src" is set in a different event loop from where the image object is (otherwise) initialized.

editHere is that same fiddle, but modified to skip the timeout. In IE8, you should notice that the even-numbered images often don't get a call made to the "load" handlers.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • I'm really unsure what you're seeing where you think IE8 fails to call the load handler without using the timeout and without nonce. In your jsFiddle without the nonce and without the timeout, I always see the load handler called in my copy of IE8. I have a javascript slideshow that relies on the load handler (it won't display an image if the load handler doesn't fire) and it's in use on hundreds of web sites (including my own) and I've never had a report of a problem with images loading on IE8. Something else must be going on when you see it fail. – jfriend00 Aug 03 '11 at 00:09
  • FYI, this would be a little simpler to get to the root issue if you didn't use jQuery for the load handler portion of your examples. Since we're talking about the naked load handler, it would be nice if that's all that was involved. – jfriend00 Aug 03 '11 at 00:12
  • Here's my pure JS fiddle: http://jsfiddle.net/jfriend00/YU56G/ that loads 6 images with onload handlers and no nonces and no use of setTimeout. I cannot get it to miss an onload handler in IE8. @Pointy - do you see failures in it in IE8? – jfriend00 Aug 03 '11 at 00:57
  • Thanks for the suggestions @Pointy. Just a note that a delay of 1 is not recommended by Mozilla, so watch out for that (The minimum delay, DOM_MIN_TIMEOUT_VALUE, is 4 ms (stored in a preference in Firefox: dom.min_timeout_value), with a DOM_CLAMP_TIMEOUT_NESTING_LEVEL of 5ms.) Also, did you mean to use a closure as the first argument in setTimeout()? – Tom Auger Aug 04 '11 at 14:31