Reputation: 77
This may be a common error, but I can't find another way of doing this. I'm creating a timeline showing multiple projects using timeline.js
with the following code
function createTimeline(){
for(var length = data.section.length,i = 0; i < length; i++){
var divWrapper = document.createElement("div");
if(i != data.section.length - 1)
divWrapper.setAttribute('class','timelineWrapper');
$('body').append(divWrapper);
layer[i] = new links.Timeline(divWrapper);
links.events.addListener(layer[i], 'rangechange',
function(){
var that = this;
var range = layer[i].getVisibleChartRange();
for (var j = 0; j < layer.length; j++){
if(j != i){
layer[j].setVisibleChartRange(range.start, range.end);
}
}
}
);
layer[i].draw(data.section[i].project, options);
}
}
It gives the error Cannot call method 'getVisibleChartRange' of undefined
.
What is the problem here? Why is layer[i] undefined? It is not being detected during the event rangechange itself.
Upvotes: 0
Views: 629
Reputation: 111
The issue is caused because the unnamed function used as event handler uses its parent scope's i variable. At the end of your loop, i==data.section.length in this scope.
This is also the i value for all your events handlers. Because of this, layer[i] is undefined, and this is causing the error message.
The easiest way to address this issue is to create a functionBuilder function taking i as parameter and returning a new function (your handler). In this returned handler's direct parent's scope, i value will be the parameter you passed to the functionBuilder function.
In case you don't understand why this works, or what closures, scopes or bind means, here is an old but complete explanation: http://blog.niftysnippets.org/2008/02/closures-are-not-complicated.html
Upvotes: 1
Reputation: 10489
You must bind i
within a closure to save its value for your addListener
call, as i
is undefined when the function in your addListener
later gets called. Try replacing the third argument of your addListener
call with the below:
(function(i) {
return function() {
var that = this;
var range = layer[i].getVisibleChartRange();
// Rest of code
};
}(i)); // Anonymous function binds i in a closure
Upvotes: 1