This is a read-only archive of the Framer Community on Facebook.

What is Framer? Join the Community
Return to index
Mark Badger
Posted Apr 06 - Read on Facebook

I'd like to get this code to work so I can save "i" on the view and then access it from the click for creating dynamic menus and working with arrays, unfortunately it only logs 4, the last number for all the views when clicking on them. Actionscript guy learning JS/framer which is great

for( var i=0; i< 5; i++){
var _mc = "_mc"+i//"mc"+i
_mc = new View({ x:0, y:35*i, width:20, height:30 , style:{"background": "blue" } })
_mc.on('click', function (){
console.log(this.count)
})
}

4 Comments

Johannes Eckert

Could it be that it stops at 4 because you never let it reach 5 (I<5)

Mark Badger

I want to assign the value of i to each button so that I can then when clicking on a button use that value to pull data out of an array, it logs 4 when it's clicked on because its referencing the last number in the for loop.

Tisho Georgiev

You should read this article on closures: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures

I'm guessing in the console.log line you really meant to write "console.log(i)", instead of "console.log(this.count)". The problem is that the i you're referencing inside the click handler is exactly the same i that you're working on in the for loop, not a copy of it. That i variable will continue to exist after the for loop, so when you execute that click handler, its value will be whatever its last value was after the loop. In your example, that would be 4. There are two easy ways to get around this:

1) Replace your for loop with a .map() call:
[0, 1, 2, 3, 4, 5].map(function(number, i) {
... [same code you had before] ...
})

.map()'s callback will create another function scope, which means that the value of i inside that function will be what you'd expect for all closures defined inside of it. This usually makes more sense when iterating through an array of views, and not a spelled-out range like I've done in the example.

2) Avoid creating the closure by using a function that accepts i as a parameter and returns your desired click handler. Here's what that would look like:

for(var i=0; i<5; i++) {
var _mc=...
_mc.on('click', function(j) { return (function() { console.log(j) } })(i))
}

Notice that in the second part of .on('click', handler), you're actually executing a function with i as a parameter that returns a function that will output the value of j (yup, that's a mouthful). When you execute it, the value of j will be whatever the value of i was when the function was created, which would be different for every iteration of the loop.

3) Create a bound function with i as a parameter to the bound function. This is another way of doing 2), really. The syntax is a little nicer.

_mc.on('click', function(j) { console.log(j); }.bind(this, i))

But really, read up on closures.

Mark Badger

Thanks I figured it was some aspect of JS, I didn't understand. I really appreciate the reply!

Read the entire post on Facebook