Javascript function to fire after images have finished loading (no jQuery) - javascript

I have the following code (the relevant parts):
window.addEventListener("load", loadImages, false);
var imgl = new Array();
function loadImages() {
var img = [
"img1",
"img2",
"img3",
"etc"
];
imgl[img.length-1].onload = function () {
alert();
initialise();
}
for (i = 0; i < img.length; i++) {
imgl[img[i]] = new Image();
imgl[img[i]].src = "img/"+img[i]+".png";
}
}
function initialise() {
//...
}
and I want to fire the initialise() function after all the images have finished loading, but nothing I tried worked. (Btw the alert() in the example is for testing, and I'm not getting it.) Why is this wrong? Other methods of doing this are also appreciated.
also I don't want to use jQuery, only if absolutely unavoidable.
Thanks in advance.
EDIT:
The following doesn't work either, although making more sense:
for (i = 0; i < img.length; i++) {
imgl[img[i]] = new Image();
}
imgl[imgl.length-1].onload = function () {
initialise();
}
for (i = 0; i < img.length; i++) {
imgl[img[i]].src = "img/"+img[i]+".png";
}

imgl[img.length-1] doesn't exist, therefore cannot have an "onload" property.
You initialize img1 at the beginning, but never populate it.
In other words:
img.length-1 = 3
img1[3] doesn't exist.
I'm getting the following error message:
Uncaught TypeError: Cannot set property 'onload' of undefined

Related

How to preload images for a game in the simplest way possible? (pure js, no library)

I want to have game.js which will only start execution when all the necessary images have been preloaded. The best way I can think of is something like:
var numberOfImagesToPreload = 2;
var imgsLoadedSoFar = 0;
var sources = ['player.png', 'terrain.png'];
for(let i = 0; i < sources.length; i++) {
var anImage = new Image();
anImage.src = sources[i];
anImage.onload = function() {
imgsLoadedSoFar++;
}
};
console.log(imgsLoadedSoFar);
if(imgsLoadedSoFar === numberOfImagesToPreload) {
console.log("Done, now start executing game.js");
};
Clearly this doesn't work because of async problems and I want to have preloader.js that will load the images first and then I want to launch game.js in which each image(game object) will have update and draw methods. If you think this shouldn't be done that way please suggest how. Thanks a lot.
EDIT
I am making some progress on this. Once the preloader.js loads all the resources I call a function in game.js function(image) but still not what I am looking for, which is basically once game.js starts loaded ALL the images should be loaded already.
Move the if inside your loop?
for(let i = 0; i < sources.length; i++) {
var anImage = new Image();
anImage.src = sources[i];
anImage.onload = function() {
imgsLoadedSoFar++;
if(imgsLoadedSoFar === numberOfImagesToPreload) {
console.log("Done, now start executing game.js");
};
}
};
Add a global boolean variable to switch to true only after preloading, wrap the functions on game.js in an if statement, once the boolean becomes true make call the function in game.js

Image onload call on an object property

I have a problem calling the onload() function of a new image object when that image is immediately set as an object property. My parser tells me that imageMap[featureId].onload is not a function. I don't get why the imageMap[featureId] isn't synonymous with the image itself? The resourceList is used as an argument to preloadResource(). I have a setinterval elsewhere waiting for the loaded count to equal the resource count. Any ideas please?
var resourceList = ["path.png","path.png","path.png","path.png"];
var loadedResource = 0;
function preloadResource(resArr)
{
var buildIndex = 1;
for (i = 1; i <= resArr.length; i++)
{
var featureId = "feature" + buildIndex;
imageMap[featureId] = new Image();
imageMap[featureId].path = resArr[(i - 1)];
buildIndex++;
imageMap[featureId].onload(function () {loadedResource++;})
imageMap[featureId].src = resArr[(i -1)];
}
}
The error is telling you the problem: "onload is not a method."
imageMap[featureId].onload = function () {loadedResource++;};
or use addEventListener

Mootools "Cannot read property 'call" of undefined"

I've got a website that is using MooTools. I've been getting the following errror, but can't figure out what is causing it. I've had very little luck tracing it out. Does anyone know what this might be? I haven't been able to find anything on the web about it.
Uncaught TypeError: Cannot read property 'call' of undefined mootools-core.js:4497
condition mootools-core.js:4497
defn mootools-core.js:4511
Stackoverflow has this question, but it does not relate as far as I can tell. Ideas?
Update
Having looked at this a bit more, the cause of the error is still mysterious. The offending line in my code is an addEvent call:
window.addEvent('load', preloader(preload));
The variable preload is an array of image urls. And the callback preloader is a method that preloads the images specified in preload. Here's the preloader method:
/**
* Event callback that preloads images
*/
function preloader(images) {
var img;
if ( images ) {
for (var i=0; i<images.length; i++) {
img = new Image();
img.src = images[i];
}
}
}
The line of mootools code specified by the error is this:
addEvent: function(type, fn){
var events = this.retrieve('events', {});
if (!events[type]) events[type] = {keys: [], values: []};
if (events[type].keys.contains(fn)) return this;
events[type].keys.push(fn);
var realType = type,
custom = Element.Events[type],
condition = fn,
self = this;
if (custom){
if (custom.onAdd) custom.onAdd.call(this, fn, type);
if (custom.condition){
condition = function(event){
//error here--> if (custom.condition.call(this, event, type)) return fn.call(this, event);
return true;
};
}
if (custom.base) realType = Function.from(custom.base).call(this, type);
}
var defn = function(){
return fn.call(self);
};
var nativeEvent = Element.NativeEvents[realType];
if (nativeEvent){
if (nativeEvent == 2){
defn = function(event){
event = new DOMEvent(event, self.getWindow());
if (condition.call(self, event) === false) event.stop();
};
}
this.addListener(realType, defn, arguments[2]);
}
events[type].values.push(defn);
return this;
},
Er. you are not passing a function as callback.
this:
window.addEvent('load', preloader(preload));
/**
* Event callback that preloads images
*/
function preloader(images) {
var img;
if ( images ) {
for (var i=0; i<images.length; i++) {
img = new Image();
img.src = images[i];
}
}
}
it will essentially invoke the preloader function immediately, not onload - and it will try to bind the event to the result of the preloader function, which does not return anything at all.
when the interpreter sees preloader(preload), it just runs it straight away. you can return a function or better yet, rewrite to:
window.addEvent('load', function(){ preloader(preload); });
// or even
window.addEvent('load', preloader.bind(this, preload));
Running example:
var imagesArray = new Array(50).join(',').split(',');
imagesArray = imagesArray.map(function(el, i){
return 'http://dummyimage.com/600x400/000/' + (255 - i) + '?' + +new Date();
});
function preloader(images) {
var img;
if ( images ) {
for (var i=0; i<images.length; i++) {
img = new Image();
img.src = images[i];
console.log(img.src);
}
}
}
window.addEvent('load', function(){
preloader(imagesArray);
});
<script src="//cdnjs.cloudflare.com/ajax/libs/mootools/1.5.0/mootools-core-full-nocompat.js"></script>
You can also have a look at my preloader class which gives you greater flexibility over how your images are pre-loaded, as well as progress etc. https://github.com/DimitarChristoff/pre-loader - it will actually wait for the images to download, allow you to choose how they are loaded etc.
mootools-more also has Asset.images you can use.
finally, not sure you want to bind to load event, which will trigger when all assets, including images, have been loaded, you should be able to start at domready instead.
It means, I believe, that mooTools is getting an undefined/malformed parameter in one of your call.
Find out which function is defined at line 4497 of mootools-core.js (can be an object method) and look for each call in your script. Log all parameters/object you're working with, and you'll find your error ;)
Edit
Seeing your code, I think your problem come from the fact that type is not declared inside condition. Try :
if (custom.condition){
condition = function(event,type){
if (custom.condition.call(this, event, type)) return fn.call(this, event);
return true;
};

Javascript reference in loop: "Uncaught TypeError: Cannot read property 'value' of undefined"

I tried debugging my code for like a few hour but I got nothing out of it. The issue is that it makes absolutely no sense on why it reports an error every time I tried to use document.forms[0][i] (i as the iterator) in the event listener but "this" satisfies the code.
//broken
var addListeners = function() {
var i;
var formFields = document.forms[0];
var formSubmit = formFields["submit"];
for (i = 0; i < formFields.length; i++) {
if (formFields[i] != formSubmit) {
formFields[i].onblur = (function () {
checkNonEmpty(formFields[i]);
});
}
}
};
//works
var addListeners = function() {
var i;
var formFields = document.forms[0];
var formSubmit = formFields["submit"];
for (i = 0; i < formFields.length; i++) {
if (formFields[i] != formSubmit) {
formFields[i].onblur = (function () {
checkNonEmpty(this);
});
}
}
};
Wouldn't "this" refer to document.forms[0][i]?... formFields references to document.forms[0]. However the exact same code (with "this" where formFields[i] is at) works just fine.
Here is the demo: http://jsfiddle.net/PbHwy/
Cranio's answer already contains the root of the matter. To get rid of this you can either include formFields[i] by using closures
var blurCallbackGenerator = function(element){
return function () {
checkNonEmpty(element);
};
};
formFields[i].onblur = blurCallbackGenerator(formFields[i]);
/* // dense version:
formFields[i].onblur = (function(element){
return function () {
checkNonEmpty(element);
};
})(formFields[i]);
*/
or simply using this.
See also:
MDN: Creating closures in loops: A common mistake
Because you define formFields in a scope outside (or better, different than) the event listener. When the event listener is called, it is called not in the addListeners function where you define formFields, but "independently", so the reference is lost and its value is undefined (but this works because it is not dependent on that scope).
The problem is that the variable i (referred to in each of your handlers) is the exact same variable in each of them, which by the time the loop has finished has value formFields.length+1 and is therefore wrong for all of them. Try this instead [note: the below used to say something VERY WRONG before I edited it -- thanks to Zeta for pointing out my mistake]:
var addListeners = function() {
var i;
var formFields = document.forms[0];
var formSubmit = formFields["submit"];
for (i = 0; i < formFields.length; i++) {
if (formFields[i] != formSubmit) {
formFields[i].onblur = (function(j) {
return (function () {
checkNonEmpty(formFields[j]);
})(i);
});
}
}
};
and you'll find it works (unless there's another bug that I haven't noticed).
If you can afford to support only Javascript 1.7 and above, you can instead write your old code but make your for look like this: for (let i=0; i<formFields.length; i++). But you quite possibly can't.

Loading and removing images with DOM, Javascript

I'm new to DOM and I'm having trouble removing multiple images that I loaded
this loads 7 images (1.jpg, 2.jpg, etc..)
function loadImages() {
for (var i = 1; i < 8; i++ ) {
var img = document.createElement("img");
img.src = "images/"+i+".jpg";
var idName = "img"+i+"";
document.getElementById(idName).appendChild(img);
}
}
this should remove all of them, but doesn't.. I think it breaks at the removeChild line.
function removeImages() {
for (var i = 1; i < 8; i++ ) {
var idName = "img"+i+"";
var d = document.getElementById(idName);
d.removeChild(document.getElementById(img));
}
}
I think I'm doing something fundamentally wrong, because I don't fully understand how this works...
In the removeImages function the variable img is not initialized. And even if it was you don't set the id for any of the images in the loadImages function. You could modify your code like this:
function loadImages() {
for (var i = 1; i < 8; i++ ) {
var img = document.createElement("img");
img.src = "images/"+i+".jpg";
img.id = i;
var idName = "img"+i+"";
document.getElementById(idName).appendChild(img);
}
}
function removeImages() {
for (var i = 1; i < 8; i++ ) {
var d = document.getElementById(i);
d.parentNode.removeChild(d);
}
}
Your code example implies that you're trying to remove a child element of the image and not the image itself. Also, you're using the wrong variable to reference the image. Maybe it works when you replace this line:
d.removeChild(document.getElementById(img));
With this:
d.parent.removeChild(document.getElementById(idName));
Also, if you're not that familiar with the DOM tree, you might want to take a look at jQuery, which is more browser-independent than regular DOM instructions.
If you are doing DOM manipulation, I would recommend that you use jQuery (or at least try it out).
It will make your life more pleasant, you will be a happier person and it will prevent you from committing harakiri.

Categories