1

The following code works in everything that I need but IE8. Everything executes up until the closure on the .load, the returning function never gets executed.

entries.resize_images = function(imgs) {
    for (var x = 0; x < imgs.length; x++) {
        var img = imgs[x];
        var pic_real_width, src;
        src = $(img).attr("src");
        $("<img/>") // Make in memory copy of image to avoid css issues
            .attr("src", src)
            .load(function(i) {
                return function () {
                    pic_real_width = this.width;
                    $(i).css("width", Math.round(pic_real_width * img_ratio));
                };
            }(img));
    }
};

Any help would be greatly appreciated!

craineum
  • 187
  • 1
  • 2
  • 7
  • So, your problem actually isn't with the closure, but that there is no `load` event fired on the detached node? What "css issues" did you try to avoid with that? – Bergi Oct 10 '12 at 17:56
  • `.load` event handler has been deprecated. Also, check out the [jQuery docs](http://api.jquery.com/load-event/) to see a list of caveats when trying to use the `.load` event with images. – MrOBrian Oct 10 '12 at 18:00
  • @Bergi Ok, I will play with that. I was following [this solution](http://stackoverflow.com/questions/318630/get-real-image-width-and-height-with-javascript-in-safari-chrome). The issue I am trying to solve is multifaceted. Have a list of images of similar but varying sizes that need to keep at a relative height. Images are lazy loaded since there can be thousands, and paged through with jPages jQuery plugin. – craineum Oct 10 '12 at 18:21
  • The .load is being called. I can put an alert before the return and see it. Unless I am missing something? – craineum Oct 10 '12 at 18:40
  • @MrOBrian, Thanks for the link. I think the caveats are what I am running into :( Any idea of a work around? – craineum Oct 10 '12 at 18:43
  • I posted a solution that does a 3-way check after reading your comments – japrescott Oct 10 '12 at 22:40

2 Answers2

0

Below is how I would approach the problem. I found reading through your version a bit confusing with regards to the particular mix of variable scope, passed params and jQuery applied params - this might be why you were finding it hard to debug.

  1. Avoid using closures if you don't need to, they can be complicated, prone to errors, and cause memory leaks if you're not careful.
  2. When applying src attributes to images I think it is better to apply the load listener before doing so, but this could just be make-believe, it's just something I've always thought would make sense.
  3. Use the power of jQuery where you can as it makes your life easier :)
  4. You shouldn't need to use a temporary image to avoid css sizing issues.
  5. IMG.complete is your friend with regards to image caching problems.

Which lead me to the following:

var resizeImages = function( imgs, img_ratio ){
  /// not sure what you're passing to this function but the following
  /// should handle an array of images elms or a jQuery collection of images
  var images = ( imgs instanceof jQuery ? imgs : $().pushStack(imgs) );
  var resizeImage = function( img ){
    var cn, st, ow;
    /// make it so we can call this function in two ways
    if ( !img ) { img = $(this); }
    /// store items of the image
    cn = img.attr('class');
    st = img.attr('style');
    /// remove those items so they don't get in the way of original width
    img.attr('class','').attr('style','');
    /// get the original width
    ow = img.width();
    /// place back the things we've removed - changes wont render visibly
    /// till code execution finishes at the end of this function call.
    img.attr('class',cn);
    img.attr('style',st);
    /// apply the resize
    img.css('width', ow * img_ratio );
  }
  images.each(function(){
    var img = $(this);
    /// check if the image is already loaded, if so, resize directly
    if ( img.get(0).complete ) {
      resizeImage(img);
    }
    /// if the image isn't loaded yet, rely on an image load event
    else {
      img.load(resizeImage);
    };
  });
};

Now the less wordy version :)

var resizeImages = function( images, scale ){
  var cn, st, ow, resizeImage = function( img ){
    img = img || $(this);
    cn = img.attr('class');
    st = img.attr('style');
    ow = img.attr('class','').attr('style','').width();
    img.attr('class',cn).attr('style',st).css('width', ow * scale );
  }
  ( images instanceof jQuery ? images : $().pushStack(images) )
    .each(function(){
      ( this.complete ? resizeImage($(this)) : $(this).load(resizeImage) );
    });
};

$(function(){
  resizeImages( document.images, 0.5 );
});
Pebbl
  • 34,937
  • 6
  • 62
  • 64
  • Thanks for the reply and effort! This doesn't work for me because of the lazy loading functionality that I need. I have to make a copy of the image, because the image might not be loaded at the time this is called. Because of that the width call could return the "place holder" image sizes or the real sizes. I tried to make it work, but was unable to. – craineum Oct 12 '12 at 17:01
  • I'm not quite sure what you mean. If you are meaning you are loading images in at different times - and you need a way to differentiate between the images that have been processed by the above code or not - all you need do is add a class to the images that have been scaled and avoid them for next time. The above code should handle the fact that images may or may not be loaded yet... becuse it makes use of the IMG.complete attribute. Any way you could show more of your code, or may be a link? There is no reason why the above code should fail and I'm sure the problem could be resolved. – Pebbl Oct 12 '12 at 17:32
-3

//edit; after learning that jQuery does magic on the load method, I removed my answer and changed my code in reply to the comments given.

We'll call our magic 3 times; add a Timeout incase we are to soon on our initial call so that if its actually cached does your magic or latest when the img loads.

var img=$("<img/>");
    img
        .attr("src", src)
        .load( function(ielem) {
                var called=false,
                    doMagic=function(e) {
                        if ( !called && this.complete ){
                            //your code
                            called=true;
                            clearTimeout(tout);                                
                        }
                    },
                    tOut=setTimeout(doMagic,100);
                //try a direct call first.. whynot? browser may be ready
                doMagic.call(ielem);
                return doMagic; 
            }(img[0])
        );

yeah, i'm more the native javascripter vs jQueryster, as you can tell. Its not really tested, i'm only showing the way. Comments welcome. Btw. just add +(new Date()).getTime() behind your src to refetch and always trigger the load event. If you dont mind the bandwidth.

japrescott
  • 4,736
  • 3
  • 25
  • 37
  • 1
    http://api.jquery.com/load-event/ "This event can be sent to any element associated with a URL: images, scripts, frames, iframes, and the window object." – VVV Oct 10 '12 at 18:01
  • 1
    This is wrong. See here. http://jsfiddle.net/wZGd6/ and here http://api.jquery.com/load-event/ – jessegavin Oct 10 '12 at 18:02
  • please see my updated code in reaction to caching and jquery load. – japrescott Oct 10 '12 at 22:34