JQuery variable scope in function

I have a problem with jQuery.

I wanted to get the real width / height img

.
Now I have used this function to get it:

var imageSize = function(image) {
    var realImageSize = { };

    $("<img/>") // Make in memory copy of image to avoid css issues
    .attr("src", $(image).attr("src"))
    .load(function() {
        realImageSize.width = this.width;   // Note: $(this).width() will not
        realImageSize.height = this.height; // work for in memory images.

        alert(realImageSize['height']); // Valid height
    });

    alert(realImageSize['height']); // Undefined

    return realImageSize;
}

      

So, I copy the image into memory and then get and set the actual size of the image in a method .load

.

Now the problem is that the variable is var realImageSize

not available in this scope for some reason. Can anyone tell me why?

+3


source to share


4 answers


This is not a scope issue, this is normal, but a synchronicity issue: you are reading the value before the load callback has been executed.

The callback that you pass as an argument load

is not executed immediately, but only after the image is loaded.

The usual solution is not to return a value, but to provide a callback:



var fetchImageSize = function(image, callback) {
    $("<img/>") // Make in memory copy of image to avoid css issues
    .load(function() {
        var realImageSize = { };
        realImageSize.width = this.width;   // Note: $(this).width() will not
        realImageSize.height = this.height; // work for in memory images.
        callback(realImageSize);
    }).attr("src", image.src);
};

fetchImageSize(myImage, function(realImageSize){
    alert(realImageSize['height']); // NOT Undefined
});

      

Note that you should always set src after setting the callback onload

. Some browsers, if you have an image in the cache, may not call the onload callback if you set it after installing the source.

+6


source


your second alert is called before the download function completes. Use callbacks ...

try it



var imageSize = function(image,callback) {
  var realImageSize = { };

  $("<img/>") // Make in memory copy of image to avoid css issues
  .attr("src", $(image).attr("src"))
  .load(function() {
    realImageSize.width = this.width;   // Note: $(this).width() will not
    realImageSize.height = this.height; // work for in memory images.

    alert(realImageSize['height']); // Valid height
    callback(realImageSize);
  });


}

imageSize(img, function(realImageSize) {
   alert(realImageSize.height);
});

      

+2


source


Use a callback. Other answers explain the async stuff, I'm not repeating it :-)

var imageSize = function(image, callback) {
.
.
.
    .load(function() {
        realImageSize.width = this.width;   // Note: $(this).width() will not
        realImageSize.height = this.height; // work for in memory images.

        callback(realImageSize);
    });

      

Then you can call the function like this:

imageSize(img, function(size) {
    alert(size.height);
});

      

+2


source


This is a common problem for a Javascript programmer who comes from other languages. Javascript uses a execution model called asynchronous calls. In a nutshell, when you register a function for an event .load()

, execution of the function will be deferred until the image is loaded , while the rest of the function body (i.e. the second alert()

) is executed immediately:

var imageSize = function(image) {
    //runs now
    var realImageSize = { };

    //runs now
    $("<img/>") // Make in memory copy of image to avoid css issues
    .attr("src", $(image).attr("src"))
    .load(function() {
        //this code can run say 5 sec later
        realImageSize.width = this.width;   // Note: $(this).width() will not
        realImageSize.height = this.height; // work for in memory images.

        alert(realImageSize['height']); // Valid height
    });

    //runs now. The realImageSize variable is yet to be created in 5 sec
    alert(realImageSize['height']); // Undefined

    //runs now
    return realImageSize;
}

      

A common pattern for solving this problem is to pass a callback to a function, which will take a long time.

//** Get an image URL and a callback function that will be called when the image is loaded and the size is detected
function imageSize ( src, callback ) {
    $("<img src="' + src + '"/>").load( function () {
        //here we call the callback which will be called when the image is loaded
        callback({
            width: this.width,
            height: this.height
        });
    });
}

imageSize( 'my/image/path.png', function ( imageSize ) {
    //this callback function will be called when the image is loaded
    alert( 'width: ' + imageSize.width + ', height: ' + imageSize.height );
});

      

+2


source