somehow my counter variable is not passed to the child function. i'm guessing it's because of some asyncronous behavior but actually i have no clue.
please help.
$(document).ready(function() {
var imgArray = new Array();
$("canvas").each(function(i) {
imgArray[i] = new Image();
});
$.each(imgArray, function(i) {
alert(i);
//correct output
this.onload = function() {
alert(i);
//"undefined"
var width = this.width,
height = this.height;
var context = $("canvas")[i].getContext("2d");
//here's the error
/* more code */
};
this.src = "PATH";
});
});
so how can i pass the value for the right canvas?
thanks for any help!
The problem you're experiencing is due to the nature of how JavaScript supports closures. That is not to say that the problem is a bug; it's behaving exactly as it's should. The onload method is being executed after i has already been iterated all the way through and becomes undefined. A viable solution is to use closures to your advantage and wrap the function creation in a call, such as this.onload = (function(index) { /* ... */ })(i);
This guarantees that the value is stored as expected in an variable internally accessible to the onload methods you're creating.
correct this part of yours to this part.
this.onload = (function(a) {
alert(a);
//"undefined"
var width = this.width,
height = this.height;
var context = $("canvas")[a].getContext("2d");
//here's the error
/* more code */
})(i);
I created a closure so that the i value will be catched as it was on the loop iteration.
I will supply a simple example :
var i=0;
i++;
alert(i) //1
i++;
alert(i) //2
alert(i) //2 !!!
this is what actually happens in you code.
Related
I am using modular pattern of javascript and trying to do things in Javascript way rather than Jquery
myapp.module1 = (function($){
"use strict";
var _config = {
backgroundImages : document.getElementsByClassName('img_paste'),
}
for(var i = 0;i < _config.backgroundImages.length; i++){
var imageElement = _config.backgroundImages[i];
imageElement.addEventListener('click',myapp.module2.addBackgroundImage(imageElement),false);
}
// $('.img_paste').click(function(){
// var img = this;
// console.log(this);
// console.log($(this));
// myapp.module2.addBackgroundImage(img);
// });
})(jQuery);
In the above code, the Jquery click function works but not the Javacript one.
When I tried to debug, I tried to console out the image in addBackgroundImage() function.
var addBackgroundImage = function(imageToBeAdded){
console.log(imageToBeAdded);//
_addImageToCanvas(imageToBeAdded);
}
The function seems to be executing even before onclick. Why is that happening?
First, the images elements appear to be empty in the console, then after some some the image elements are displayed in console.
Take a look at this simple code example:
function describeTheParameter(p) {
console.log("describeTheParameter invoked. p is of type " + typeof(p));
}
function stringFunction() {
return "Hello World!";
}
describeTheParameter(stringFunction());
describeTheParameter(stringFunction);
This results in
describeTheParameter invoked. p is of type string
describeTheParameter invoked. p is of type function
In the first call, we are calling stringFunction, and then passing the result to describeTheParameter.
In the second call, we are actually passing the function to describeTheParameter.
When you call addEventListener you must follow the pattern of the second call: pass the function without invoking it:
In the following line of code, you are invoking addBackgroundImage, and then passing the result (which will be undefined) to addEventListener.
imageElement.addEventListener('click',myapp.module2.addBackgroundImage(imageElement),false);
You need to pass a yet-to-be-called function into addEventListener.
The smallest step to make your code work is to employ a currying function:
function addImage(imageElement) {
return function() {
myapp.module2.addBackgroundImage(imageElement);
}
}
for(var i = 0;i < _config.backgroundImages.length; i++){
var imageElement = _config.backgroundImages[i];
imageElement.addEventListener('click', addImage(imageElement), false);
}
For much simpler code, make use of the this keyword. In this case, this will point to the element that's firing the event.
function imageClickHandler() {
var imageElement = this;
myapp.module2.addBackgroundImage(imageElement);
}
for(var i = 0;i < _config.backgroundImages.length; i++){
var imageElement = _config.backgroundImages[i];
imageElement.addEventListener('click', imageClickHandler, false);
}
The function seems to be executing even before onclick. Why is that happening?
Look at the statement you wrote:
myapp.module2.addBackgroundImage(imageElement)
You are calling the function and then passing its return value as the function argument.
You want something more along the lines of:
myapp.module2.addBackgroundImage.bind(myapp.module2, imageElement)
(or the function expression that you used in the commented out code)
I have read countless of answers of this issue and I came up with the following, but it doesn't work either.
function fitToParent(objsParent, tagName) {
var parent, imgs, imgsCant, a, loadImg;
//Select images
parent = document.getElementById(objsParent);
imgs = parent.getElementsByTagName(tagName);
imgsCant = imgs.length;
function scaleImgs(a) {
"use strict";
var w, h, ratioI, wP, hP, ratioP, imgsParent;
//Get image dimensions
w = imgs[a].naturalWidth;
h = imgs[a].naturalHeight;
ratioI = w / h;
//Get parent dimensions
imgsParent = imgs[a].parentNode;
wP = imgsParent.clientWidth;
hP = imgsParent.clientHeight;
ratioP = wP / hP;
//I left this as a test, all this returns 0 and false, and they shouldn't be
console.log(w);
console.log(h);
console.log(ratioI);
console.log(imgs[a].complete);
if (ratioP > ratioI) {
imgs[a].style.width = "100%";
} else {
imgs[a].style.height = "100%";
}
}
//Loop through images and resize them
var imgCache = [];
for (a = 0; a < imgsCant; a += 1) {
imgCache[a] = new Image();
imgCache[a].onload = function () {
scaleImgs(a);
//Another test, this returns empty, for some reason the function fires before aplying a src to imgCache
console.log(imgCache[a].src);
}(a);
imgCache[a].src = imgs[a].getAttribute('src');
}
}
fitToParent("noticias", "img");
To summarise, the problem is the event onload triggers before the images are loaded (or that is how I understand it).
Another things to add:
I don't know at first the dimensions of the parent nor the child,
because they varied depending of their position on the page.
I don't want to use jQuery.
I tried with another function, changing the onload event to
window, and it worked, but it takes a lot of time to resize because
it waits for everything to load, making the page appear slower,
that's how I came to the conclusion the problem has something to do
with the onload event.
EDIT:
I made a fiddle, easier to look at the problem this way
https://jsfiddle.net/whn5cycf/
for some reason the function fires before aplying a src to imgCache
Well, the reason is that you are calling the function immedeatly:
imgCache[a].onload = function () {
}(a);
// ^^^ calls the function
You call the function and assign undefined (the return value of that function) to .onload.
If you want to use an IIFE to capture the current value of a, you have to make it return a function and accept a parameter to which the current value of a is assigned to:
imgCache[a].onload = function (a) {
return function() {
scaleImgs(a);
};
}(a);
Have a look again at JavaScript closure inside loops – simple practical example .
I was asked the below question during an interview, and I still couldn't get my head around it, so I'd like to seek your advice.
Here's the question:
var countFunctions = [];
for(var i = 0; i < 3; i++){
countFunctions[i] = function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
}
//The below are executed in turns:
countFunctions[0]();
countFunctions[1]();
countFunctions[2]();
When asked what would be the output of the above, I said count0,count1 and count2 respectively. Apparently the answer was wrong, and that the output should all be count3, because of the concept of closures (which I wasn't aware of then). So I went through this article and realized that I should be using closure to make this work, like:
var countFunctions = [];
function setInner(i) {
return function(){
document.getElementById('someId').innerHTML = 'count' + i;
};
}
for(var i = 0; i < 3; i++){
countFunctions[i] = setInner(i);
}
//Now the output is what was intended:
countFunctions[0]();//count0
countFunctions[1]();//count1
countFunctions[2]();//count2
Now that's all well and good, but I remember the interviewer using something simpler, using a self-executing function like this:
var countFunctions = [];
for(var i = 0; i < 3; i++) {
countFunctions[i] = (function(){
document.getElementById('someId').innerHTML = 'count' + i;
})(i);
}
The way I understand the above code, we are skipping the declaration of a separate function and simply calling and executing the function within the for loop.
But when I ran the below:
countFunctions[0];
countFunctions[1];
countFunctions[2];
It didn't work, with all the output being stuck at count2.
So I tried to do the below instead:
for(var i = 0; i < 3; i++) {
countFunctions[i] = function(){
document.getElementById('someId').innerHTML = 'count' + i;
};
}
, and then running countFunctions[0](), countFunctions[1]() and countFunctions[2](), but it didn't work. The output is now being stuck at count3.
Now I really don't get it. I was simply using the same line of code as setInner(). So I don't see why this doesn't work. As a matter of fact, I could have just stick to the setInner kind of code structure, which does work, and is more comprehensive. But then I'd really like to know how the interviewer did it, so as to understand this topic a little better.
The relevant articles to read here are JavaScript closure inside loops – simple practical example and http://benalman.com/news/2010/11/immediately-invoked-function-expression/ (though you seem to have understood IEFEs quite well - as you say, they're "skipping the declaration of a separate function and simply calling and executing the function").
What you didn't notice is that setInner does, when called, return the closure function:
function setInner(i) {
return function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
}
// then do
var countFunction = setInner("N"); // get the function
countFunction(); // call it to assign the innerHTML
So if you translate it into an IEFE, you still need to create (and return) the function that will actually get assigned to countFunctions[i]:
var countFunctions = [];
for(var i = 0; i < 3; i++) {
countFunctions[i] = (function(i){
return function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
})(i);
}
Now, typeof countFunctions[0] will be "function", not "undefined" as in your code, and you can actually call them.
Take a look at these four functions:
var argument = 'G'; //global
function passArgument(argument){
alert(argument); //local
}
function noArguments(){
alert(argument); //global
}
function createClosure_1(argument){
return function (){
alert(argument); //local
};
}
function createClosure_2(argument){
var argument = argument; //local
return function (){
alert(argument); //local
};
}
passArgument('L'); //L
noArguments(); //G
createClosure_1('L') //L
createClosure_2('L') //L
alert(argument) //G
I think, first function is obvious.
In function noArguments you reference the global argument value;
The third and fourth functions do the same thing. They create a local argument variable that doesn't change inside them and return a function that references that local variable.
So, what was in the first and the last code snippet of your question is a creation of many functions like noArguments,
that reference global variable i.
In the second snippet your setInner works like createClosure_1. Within your loop you create three closures, three local variables inside them. And when you call functions inside countFunctions, they get the value of the local variable that was created inside the closure when they were created.
In the third one you assign the result of the execution of those functions to array elements, which is undefined because they don't return anything from that functions.
I'm trying to find out more about closures in Javascript and was going through this: https://developer.mozilla.org/en/JavaScript/Guide/Closures#Practical_closures
According to this article, by using such a function:
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
We can then make use of such statements to increase/decrease the font-size of text on a page:
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
While I understand the concept here - i.e. size12, size14 and size16 become closures that allow access to the internal function, I can't help but feel that this is unnecessary. Isn't it easier to just have:
function makeSizer(size) {
document.body.style.fontSize = size + 'px';
}
, and then invoke it with these?
document.getElementById('size-12').onclick = makeSizer(12);
document.getElementById('size-14').onclick = makeSizer(14);
document.getElementById('size-16').onclick = makeSizer(16);
Can anyone tell me if my thinking is right - or maybe I'm just a novice to Javascript and doesn't understand the advantage to using closure in this scenario, in which case I'll be most glad if you can explain the advantage of doing so.
Thanks in advance guys.
No, you can't do that.
It's as if you had written:
document.getElementById('size-12').onclick = (function(size) {
document.body.style.fontSize = size + 'px';
})(12);
The function gets immediately invoked, the style will be applied straight away, and no .onclick handler gets registered because the return value of the function is undefined.
The real point of the example is to show that you can return a function from another function, and that you can then assign that result to an event handler.
If you had left makeSizer() unmodified then you could assign the handlers as proposed without intermediate variables, i.e.:
document.getElementById('size-12').onclick = makeSizer(12);
but that won't work if you change makeSizer() the way you described.
It is also less efficient than storing the "sizer" in a variable if you use the same sizer more than once.
For the example you presented, of course closure is not necessary, but I guess it is just to make it simple to present the concept. There are cases though that closure is the best solution to use: think about how to implement a "private" attribute in javascript or when you need curryng to encapsulate arguments (ie, for a callback function).
I hope the following example helps:
var makeSequencer = function() {
var _count = 0; // not accessible outside this function
var sequencer = function () {
return _count++;
}
return sequencer;
}
var fnext = makeSequencer();
var v0 = fnext(); // v0 = 0;
var v1 = fnext(); // v1 = 1;
var vz = fnext._count // vz = undefined
Yes, those variables (sizeN) are unnecessary. You can directly assign the result of makeSizer() as handlers, which looks far better.
But, the use of these variables is not the concept of closures. The closure in this example is the function makeSizer, which returns a function (even without arguments), which still has access to the size variable.
Though, you need to see the difference between
function makeSizer(size) {
return function resize() {
document.body.style.fontSize = size + 'px';
};
}
and
function resize(size) {
document.body.style.fontSize = size + 'px';
}
Executing makeSizer(5) does not do anything, it returns a function that sets the size to the pre-defined size when invoked. Instead executing resize(5) does set the size directly. You can't use the result of the latter function as an event handler.
function Something() {
this.var1 = 0;
this.var2 = 2;
this.mytimer;
this.getCars=function() {
//some code
};
this.start = function(l) {
this.updateTimer=setInterval("this.getCars();" , 5000);
};
}
var smth = new Something();
smth.start();
When I type in this.getCars() it does not work. if the function is global declared and i put in for example just getCars it works.
I don't know how to work out this problem because setInterval becomes as parameter a String.
Can somebody help me put with this?
var me = this
setInterval(function() {me.getCars()}, 5000)
if you happen to be using prototype, you could also use the handy bind method:
setInterval(this.getCars.bind(this), 5000)
Try this:
function Something() {
this.var1 = 0;
this.var2 = 2;
this.mytimer;
var me = this;
this.getCars = function() {
console.log(me.var2);
};
this.start = function(l) {
me.updateTimer = setInterval(me.getCars, 1000);
}
}
var smth = new Something();
smth.start();
The console.log() bit is Firefox/Firebug. Replace it with something else if you're not using that (although I would highly recommend developing with it).
Basically the problem is that when you call a function, even a method of an object, the way you call it determines the value of this. See Method binding for more details. So what you do is fix the value of this as I've done in the above example (for methods).