Melanie Melanie - 5 months ago 10
jQuery Question

How to resize and display images in JavaScript

I am attempting to read images from a list of paths (10 items long) returned via an ajax function, resize each one and then display one after the other on the page. However, the below code only displays the first image (resized) and none of the others. I am fairly sure the resizing works as the printed sizes look correct. Below is my code in JavaScript:

// Helper function
function scaleSize(maxW, maxH, currW, currH){
var ratio = currH / currW;
if(currW >= maxW && ratio <= 1) {
currW = maxW;
currH = currW * ratio;
} else if(currH >= maxH) {
currH = maxH;
currW = currH / ratio;
}
return [currW, currH];
}

function get_similar_images(image_name) {
console.log(image_name)
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "/get_similar",
dataType: "json",
async: true,
data: JSON.stringify({name: image_name}),
success: function(data) {
console.log(data);
image_list = data['images']
category = data['category']
for (var i=0; i<image_list.length; i++) {
var img = document.createElement("img");
img.src = "static/products/"+category+"/"+image_list[i];
var actualH;
var actualW;
var newH;
var newW;
img.onload = function(){
actualW = this.width;
actualH = this.height;
console.log(actualW, actualH)
var newSize = scaleSize(300, 300, actualW, actualH);
console.log(newSize)
img.width = newSize[0];
img.height = newSize[1];
document.getElementById('imageDiv').appendChild(img)
};
}
},
error: function(xhr, status, error) {
// console.log(xhr.responseText);
}
})
}

Answer

If we simplify your for loop like that :

var img = document.createElement("img");
img.src = image_list[0];
img.onload = function() {
  console.log(img);
};

var img = document.createElement("img");
img.src = image_list[1];
img.onload = function() {
  console.log(img);
};

You will understand where your mistake comes from. onload is an event, which means it's running asynchronously from your your code. You're updating img value with a new image element (and a new href attribute) before the first load event is triggered. Thus the onload event is actually called after the last update of the img variable, which actually happen when your reach the last loop of your for.

Why ?

Because a for loop don't create a new scope for the variable img, so each time you touch the img var, even if you're putting var in front, the original img is updated.

You should then use a function, because a function actually create a new scope for variables that are declared (var myVar) inside it :

function injectAndResize(imageUrl) {
  var img = document.createElement("img");
  img.src = imageUrl;
  var actualH;
  var actualW;
  var newH;
  var newW;
  img.onload = function() {
    actualW = this.width;
    actualH = this.height;
    // console.log(actualW, actualH)
    var newSize = scaleSize(300, 300, actualW, actualH);
    // console.log(newSize);
    this.width = newSize[0];
    this.height = newSize[1];
    console.log(img);
    console.log(document.getElementById('imageDiv').innerHTML)
    document.getElementById('imageDiv').appendChild(img)
    console.log(document.getElementById('imageDiv').innerHTML)
  };
}

for (var i = 0; i < image_list.length; i++) {
  injectAndResize(image_list[i]);
}