dylanweber
dylanweber

Reputation: 608

Why doesn't this Javascript image object get added to the image array?

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";
        }
    }
}

Upvotes: 1

Views: 171

Answers (4)

kalley
kalley

Reputation: 18462

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.

Upvotes: 2

RobG
RobG

Reputation: 147343

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
}

Upvotes: 1

Rikky
Rikky

Reputation: 515

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!
})

Upvotes: 0

Igor
Igor

Reputation: 15893

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

Upvotes: 2

Related Questions