0

Drawing a set of circle with RaphaelJS library.

For each circle I want to create an onclick function that passes a variable, but in this function my variable is undefined.

What is the problem?

This is my code:

//children is an array such as [1,2,4[5,6,7]]
for (var i = 0; i < children.length; i++) {
    var array = children;
    alert("ARRAY[0]===" + array[0])

    var st = space2Draw.set();
    st.push(space2Draw.circle(xChildren, yChildren, 20).click(function (array) {
        //array[i] is undefined
        alert("ARRAY[i]===" + array[i]);
        //retrive(array[i]);
    }),
    LineRoot(xRadice, yRadice, xChildren, yChildren, space2Draw));
    space2Draw.text(xChildren, yChildren, children[i]).attr({
        fill: "white"
    });
    st.attr({
        fill: "red"
    });

    xChildren += 50;
}
Nope
  • 22,147
  • 7
  • 47
  • 72
ilamaiolo
  • 345
  • 2
  • 4
  • 14

2 Answers2

2

You shouldn't have a "array" parameter to your click callback, it is overriding the "array" var from the parent scope. You can just remove the argument and it should be ok.

I think you will have another problem with i always being children.length in your click callback (because the function scope will actually be closed at the end of the loop. More info here). You should create an auxiliary function to create the callback for you.

You could try something like this:

//children is an array such as [1,2,4[5,6,7]]
for (var i = 0; i < children.length; i++) {
    var array = children;
    alert("ARRAY[0]===" + array[0])

    var st = space2Draw.set();
    st.push(space2Draw.circle(xChildren, yChildren, 20).click(getCallback(array , i)),
    LineRoot(xRadice, yRadice, xChildren, yChildren, space2Draw));
    space2Draw.text(xChildren, yChildren, children[i]).attr({
        fill: "white"
    });
    st.attr({
        fill: "red"
    });

    xChildren += 50;
}

function getCallback(array , i){
   return function () {
        alert("ARRAY[i]===" + array[i]);
    }
}
Community
  • 1
  • 1
Yann
  • 2,211
  • 1
  • 15
  • 14
1

You should do this:

...
st.push(space2Draw.circle(xChildren, yChildren, 20).click((function (array, i) {
    return function () {
        //array[i] is undefined
        alert("ARRAY[i]===" + array[i]);
        //retrive(array[i]);
    }
}(array, i)));
...

The reason why this works is that in javascript, scope is defined by functions, not by blocks.

Your callback gets access (by closure) to the array and i (among other variables), but when the callback gets executed, i is equal to children.length, because the loop has finished iterating.

By using IIFE (immediately invocation function expression) we create a new scope, and the callback gets access to the current value of array and i.


Also, the click function calls the callback by passing an event object as first parameter, so our IIFE should return this

return function (event) {
    // access array and i by closure
    // event is the event representing the click
    // event, provided by Raphael

    // do something
}
Alessandro Vendruscolo
  • 14,493
  • 4
  • 32
  • 41