Reputation: 127
Im trying to create a simple loop that creates 50 buttons, adds them to screen and then when a button is pressed, it traces out that number. I can get it to work by doing stuff I consider hacky (such as using the buttons X/Y location to determine its value), but I'd rather just be able to hold a single value in the function.
The code itself is:
for (var a:int = 0; a < 5; a++) {
for (var b:int = 0; b < 10; b++) {
var n = (a * 10) + b + 1;
var btt:SimpleButton = new BasicGameButton();
btt.x = 20 + b * 50;
btt.y = 50 + a * 80;
addChild(btt);
btt.addEventListener(MouseEvent.CLICK, function f() { trace(n); } );
}
}
At the moment, whenever a button is pressed, it simply outputs "50". Is there a way of "freezing" the value of n when the function is created, for that function? (BasicGameButton is just a square button, created in the flash library)
Many thanks.
Upvotes: 0
Views: 1539
Reputation: 15623
this is an AS3 flaw comming from the abandoned ECMA-Script draft AS3 is based on ... n
is scoped to the innermost function body it is declared in (thus as if it were declared before the loop), although declared in the innermost loop body of your code.
you can do the following:
var makeFunction:Function = function (n:int):Function {//this can of course also be a method
return function (event:MouseEvent):void {
trace(n);
}
}
//in the loop body, use it like this:
btt.addEventListener(MouseEvent.CLICK, makeFunction(n));
As a side note: Haxe does not have this flaw.
Upvotes: 0
Reputation: 4142
I agree with wpjmurray that this is not a flaw at all; rather it is a result of how scope works in EcmaScript.
Your click handler carries an implicit reference to a variable (n
). When the function is called (triggered by a click), the value of that variable has changed. It makes perfect sense when you think about it this way.
The solution is to store the value on each iteration and associate it somehow with your button. There are three good ways to do this:
makeFunction
creates a new method closure that has an implicit reference to a new variable (makeFunction
's n
argument) which doesn't change.Here's an example:
var dict:Dictionary = new Dictionary(true);
for (var a:int = 0; a < 5; a++) {
for (var b:int = 0; b < 10; b++) {
var n = (a * 10) + b + 1;
var btt:SimpleButton = new BasicGameButton();
btt.x = 20 + b * 50;
btt.y = 50 + a * 80;
addChild(btt);
dict[btt] = n;
btt.addEventListener(MouseEvent.CLICK, function f(event:MouseEvent) { trace(dict[event.currentTarget]); } );
}
}
Because of how scope works in EcmaScript, each click handler that you create carries an implicit reference to the dictionary, which you can use to look up the index. Now the index is mapped directly to the button, without having to add a property or create a new function.
Upvotes: 0
Reputation: 8141
I see that the answer has been selected, but I don't agree with the solution, though it does work. I wouldn't characterize this as a flaw with AS3 as this would happen the same way with Javascript, also based on ECMAScript. In any case, you can avoid the problem altogether by just creating a property for BasicGameButton that holds the value of n. Also, instead of adding a listener for every button, just create a single listener on the button container that listens for the mouse click (a mouse click "bubbles" up to a parent display object) and gets the clicked button using the event target.
/* inside the loop */
btt.n = n;
/* parent listener you only have to create once */
buttonContainer.addEventListener(MouseEvent.CLICK, onClick);
function onClick(e:Event):void {
trace(BasicGameButton(e.target).n);
}
Upvotes: 0