Pergunta

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.

Foi útil?

Solução

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

Outras dicas

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.

I'll post some code example later today, as soon as I have access to a pc (no way I can type that from a tablet :o) )

EDIT: I've been too slow... mc10 posted more or less what I wanted to post :o)

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

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top