1

I've been trying to create a small HTML5-based example, but I've ran into some problems. I want to render a rock on a <canvas> but this Javascript object won't work properly.

function ImageData() {
    this.image_names = ["rock1"];
    this.images = new Array();
    this.loadResources = function () {
        for (var i = 0; i < this.image_names.length; i++) {
            var img = new Image();
            img.onload = (function (a) {
                a.images[a.image_names[i]] = img;
                console.log(a.images); //log is successful, image is in array
            })(this);
            img.src = "images/" + this.image_names[i] + ".png";
        }
    }
}

It's use is to be as follows:

var a = new ImageData();
a.loadResources();
var rock1_image = a.images["rock1"]; //the "rock1.png" image

If you try accessing the object via console, you'll see that there is no image in the image array, after being certain the image loaded. I can't figure out why it's not working. I've looked over it multiple times.

EDIT: Here is my final, working result:

function ImageData() {
    this.image_names = ["rock1"];
    this.images = new Array();
    this.loadResources = function (callback) {
        for (var i = 0; i < this.image_names.length; i++) {
            var img = new Image();
            img.onload = (function (a, i) {
                return function() {
                    a.images[a.image_names[i]] = img;
                    if (i == (a.image_names.length - 1)) callback();
                };
            })(this, i);
            img.src = "images/" + this.image_names[i] + ".png";
        }
    }
}
3
  • The function on the RHS of the assignment to the onload property is run immediately, so the result is assigned. It doesn't return anything, so undefined is assigned. Commented Jul 13, 2013 at 3:52
  • 1
    Also why is images an array if you're using non-numeric keys. Commented Jul 13, 2013 at 3:57
  • It's easier to pull out images in an array, regardless of how they're organized, when processing multiple objects in multiple frames being generated instantly. One image gets used a few times. Commented Jul 13, 2013 at 4:02

4 Answers 4

2

I would venture to say that you are attempting to access it before it is added to your array. I would suggest adding a callback or something to your loadResources method to make sure that it's there before you attempt to access it.

this.loadResources = function (callback) {
    for (var i = 0; i < this.image_names.length; i++) {
        var img = new Image();
        img.onload = (function (a, i) {
            return function() {
                a.images[a.image_names[i]] = img;
                console.log(a.images); //log is successful, image is in array
                callback();
            };
        })(this, i);
        img.src = "images/" + this.image_names[i] + ".png";
    }
} 

then

var a = new ImageData();
var rock1_image;
a.loadResources(function() {
    rock1_image = a.images["rock1"];
    // do whatever you need to do with the image in here.
});

Also, what @Igor mentions. I totally missed that. I adjusted the code above so you could keep your this scope without having to set it elsewhere.

Updated to take care of the i issue brought up by @RobD.

Sign up to request clarification or add additional context in comments.

2 Comments

this has nothing to do with scope, it's just a variable that is set by the call and is always resolved in the current execution context.
Also, there is a closure with i from the onload function to the outer loadResources function that will cause issues later.
2

Remove (this) after onload handler definition. You are actually calling this function right away, assigning undefined (since it returns nothing) as img.onload value.

3 Comments

this is required, it references the instance (a) inside the IIFE.
@RobG then maybe the IIFE should return a function?
@JanDvorak—dunno, it might as well just be run the body as statements in the for block, removing the need to pass this to the function. The OP doesn't seem to require anything to be assigned to onload. Kalley has allowed for a callback, so job done.
1

In the code:

> function ImageData() {
>     this.image_names = ["rock1"];
>     this.images = new Array();
>     this.loadResources = function () {
>         for (var i = 0; i < this.image_names.length; i++) {
>             var img = new Image();
>             img.onload = (function (a) {

This function will be run immediately and does not return a value, so undefined will be assigned. So no point in the assignment, just run the code.

>                 a.images[a.image_names[i]] = img;

Images is an array that is used as a plain object. Better to use a plain object then.

>                 console.log(a.images); //log is successful, image is in array
>             })(this);

The outer this must be passed in because of the IIFE. Remove the IIFE and there is no requirement to pass this. And if a reference to the instance is stored in the function, it won't matter how the function is called (i.e. you won't have to set this in the call).

>             img.src = "images/" + this.image_names[i] + ".png";
>         }
>     }
> }

You might want something like:

function ImageData() {
    this.image_names = ['rock1'];
    this.images = {};
    imageData = this;
    this.loadResources = function (callback) {

        for (var i = 0; i < imageData.image_names.length; i++) {
            var img = new Image();
            imageData.images[a.image_names[i]] = img;
            img.src = 'images/' + this.image_names[i] + '.png';

            if (callback) {
                img.onload = callback;
            }
        }
    }
}

var a = new ImageData();
a.loadResources(function(){console.log('loaded: ' + this.src);}); // loaded: …

window.onload = function() {
    document.body.appendChild(a.images.rock1); // image appears in document
}

Comments

0

I haven't tested your code yet, but I guess this is the problem: You are trying to access your image before it was loaded. You can use callback event handler after it was load like:

var data = new ImageData();

data.loadResources(function(imgObj){
 // imgObj is the newly load image here. Have fun with it now!
})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.