views:

1142

answers:

2

Problem:

I am having trouble implementing a recursive image lazy load in all relevant versions of Internet Explorer. I am using jQuery 1.3.2, and the code that follows works wonderfully on Firefox, Safari, and Chrome.

While I would expect that IE6's javascript engine would choke, I am very surprised to find that it does not work at all on IE7, and only occasionally on IE8. That it works sometimes on IE8 is frustrating, because it seems to imply that if I work hard enough and set enough breakpoints in the Microsoft script debugger, I'll perhaps get it to work after some struggle.

I'm aware that I don't have to do this recursively, and I will reimplement it if I don't find a suitable solution, but the recursive approach is particularly suitable in this example because I would like the images to load one at time, prettily in a row. (And I expect a max depth of around 15)

I've come to StackOverflow with this question because I've run up against this a problem like this in the past and would like to know if anyone has any insights into what the problem may be:

  • recursion in jQuery?
  • recursion in IE[6-8]'s javascript engine?
  • faulty jQuery callbacks/methods-chaining in IE[6-8]?
  • naive implementation?

Code:

Here is the lazy-load function:

jQuery.lazyLoadImages = function(imgSelector, recursive, fadeIn)
{
  var image = $(imgSelector);
  if (image.size()) {
    image.parents(SAH.imageContentSelector).addClass(SAH.loadingClass);
    // the img src attribute is stored in the alt attribute
    var imgSrc = image.attr('alt');
    image.attr('src', imgSrc).attr('alt','').load(function() {
      $(this)
      .removeClass(SAH.lazyLoadClass)
      .parents(SAH.imageContentSelector)
      .removeClass(SAH.loadingClass);
      if (fadeIn) $(this).fadeIn(SAH.lazyLoadDuration);
      if (recursive) {
        var nextPos = eval(parseInt(imgSelector.replace(/.*position-(\d+).*/,'$1')) + 1);
        var nextImage = imgSelector.replace(/position-(\d+)/,'position-' + nextPos);
        $.lazyLoadImages(nextImage, recursive, fadeIn);
      }
    });
    return true;
  } else {
    return false;
  }
}


The SAH.* variables are just variables stored in a global object, SAH. Here is the relevant section that calls $.lazyLoadImages():

// fade the first image in with the navBar
var firstGalleryImageSelector = 'img#img-position-1-' + galleryId + '.' + SAH.lazyLoadClass;
$.lazyLoadImages(firstGalleryImageSelector,false,true);
navBar.show('slide', { direction: 'right' }, function() {
  // load the rest after the navBar callback
  $.lazyLoadImages(firstGalleryImageSelector.replace(/position-1/,'position-2'), true, true);
});

The first call to $.lazyLoadImages() is non-recursive; the second one is recursive and fires after a navigation bar slides into the window.


Finally, here is the relevant html:

<div id='position-1-i4design' class='content image' style='width:400px'>
  <div class='image-gallery'>
    <a class='local-x' href='#position-1-i4design'>
      <img alt='/images/press/i4design/i4design-1.jpg' id='img-position-1-i4design' class='lazy-load hide'>
    </a>
    ...
  </div>
  ...
</div>

<div id='position-2-i4design' class='content image' style='width:389px'>
  <div class='image-gallery'>
    <a class='local-x' href='#position-2-i4design'>
      <img alt='/images/press/i4design/i4design-2.jpg' id='img-position-2-i4design' class='lazy-load hide'>
    </a>
    ...
  </div>
  ...
</div>

<div id='position-3-i4design' class='content image' style='width:398px'>
  <div class='image-gallery'>
    <a class='local-x' href='#position-3-i4design'>
      <img alt='/images/press/i4design/i4design-3.jpg' id='img-position-3-i4design' class='lazy-load hide'>
    </a>
    ...
  </div>
  ...
</div>

...
+6  A: 

The IEs need onload Events defined for images before you attempt to set that Element's src. All other browsers will handle that just fine; the IEs will choke.

It's likely that your load function in the above will never run for that reason.

Give this a try:

   image.attr('alt','').load(function() {
      $(this)
      .removeClass(SAH.lazyLoadClass)
      .parents(SAH.imageContentSelector)
      .removeClass(SAH.loadingClass);
      if (fadeIn) $(this).fadeIn(SAH.lazyLoadDuration);
      if (recursive) {
        var nextPos = eval(parseInt(imgSelector.replace(/.*position-(\d+).*/,'$1')) + 1);
        var nextImage = imgSelector.replace(/position-(\d+)/,'position-' + nextPos);
        $.lazyLoadImages(nextImage, recursive, fadeIn);
      }
    }).attr('src', imgSrc);
ajm
Wow! That is brilliant! I wish I could give you a gold medal!
guns
Worked even in IE6. This is perfect! Thank you so much.
guns
No problem, man. That particular bug's tripped me up for HOURS before.
ajm
+1  A: 

I have done something very similar with a recursive JavaScript function to load images, which works fine in IE.

The major differences that I can see are:

  • I used a normal JavaScript function, not a jQuery function.
  • I created each image with jQuery and added it to the relevant container.

I'm not sure if either of those points matters, but just looking at your code, I'm guessing that some more expensive functions would be referencing the image's parents twice, and also fading the image in.

Does it successfully run if those are commented out?

ern
+1 Yes, it seems that your second point is related to ajm's answer.
guns