Firefox, objects containing charts, and event listeners weirdness

196 views
Skip to first unread message

asgallant

unread,
Jan 3, 2012, 5:15:13 PM1/3/12
to google-visua...@googlegroups.com
I have some code that is basically like this:

var finished {};
var listeners {};
for (in charts{
    listener[xgoogle.visualization.events.addListener(charts[x]['chart']'ready' function ({
        finished[xtrue;
        // further processing
        google.visualization.events.removeListener(listener[x]);
    });
    charts[x]['chart'].draw(charts[x]['data']charts[x]['options']); 
}

which works fine in Chrome and IE.  In Firefox, when the event listeners fire, they almost always have x equal to the last element in the charts object (occasionally, they have x equal to an earlier element, but never the correct one).  The net result is:
a) I can never tell when all of the charts have finished drawing
b) the event handlers for most of the charts stick around to potentially cause problems on redraw

Anyone have any ideas on how I can work around this?

Riccardo Govoni ☢

unread,
Jan 4, 2012, 5:04:05 AM1/4/12
to google-visua...@googlegroups.com
The 'x' you use inside the listener callback comes from the surrounding closure, therefore it is the very same loop variable. Depending on when the listener is invoked, the value of 'x' may be the last element of the charts object (if the callback is invoked after the loop completed) or some other element.
Not sure why Chrome and IE work fine, I guess it may depend in differences in the order in which callbacks are executed with respect to the caller.

Anyway, the correct way to deal with it, is to capture the 'current' x in the loop with an additional closure, like this:

for (x in charts) {
  listener[x] = g.v.events.addListener(charts[x]['chart'], 'ready' , (function(x) {
  })(x));
  ...
}

Notice the anonymous callback function which is invoked passing it the current 'x' value, ensuring the corresponding closure won't have its value changed as the loop progresses. 

See:

- R.

--
You received this message because you are subscribed to the Google Groups "Google Visualization API" group.
To view this discussion on the web visit https://groups.google.com/d/msg/google-visualization-api/-/gtuC6PYjt_oJ.
To post to this group, send email to google-visua...@googlegroups.com.
To unsubscribe from this group, send email to google-visualizati...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-visualization-api?hl=en.

asgallant

unread,
Jan 4, 2012, 9:56:40 AM1/4/12
to google-visua...@googlegroups.com
Dude, you rock!

I suppose this thread should be renamed "weird behavior in Chrome/IE," then.  So, doing as you suggest, the function executes properly in all browsers.  The problem now is that they all throw errors when you pass a closure to #addListener():

FF:

Chrome:
"undefined is not a function"

IE:
"Object expected"

This occurs regardless of whether you pass the closure in a loop or not, and whether or not there is anything in the function itself.  I plopped one of these into a jsfiddle as a test case: http://jsfiddle.net/zeXg6/

I solved the problem by wrapping the #addListener call in the closure instead: http://jsfiddle.net/zeXg6/1/

It looks ugly, but it works.

Riccardo Govoni ☢

unread,
Jan 5, 2012, 12:25:13 PM1/5/12
to google-visua...@googlegroups.com
Stupid me. The code I wrote you actually executes the function (returning nothing, hence the undefined error you get):

(function(x) { ... })(x);

while instead should return one. So either you change it to do so:

for (x in charts) {
  listener[x] = g.v.events.addListener(charts[x]['chart'], 'ready' , (function(x) {
    return function() {
      // do something with 'x'
    };
  })(x));
  ...
}

but that becomes really verbose. So you can lock the 'x' just inside the for loop and achieve the same result in a slightly more compact way:

for (x in charts) {
  (function(x) {  // 'x' is now locked by the closure
    g.v.events.addListener(charts[x]['chart'], 'ready', function() { /* your listener code */ });
  })(x);
}

Again, haven't tried the above in a live example, so let's hope I didn't write something wrong again :-).
Another reference reading, for better understanding:
( look at the 'saving state with closures' paragraph, that describes exactly the 2 techniques I mentioned above).

- R. 


--
You received this message because you are subscribed to the Google Groups "Google Visualization API" group.

asgallant

unread,
Jan 5, 2012, 12:31:32 PM1/5/12
to google-visua...@googlegroups.com
The latter is what I ended up doing.  Thanks again, man.
Reply all
Reply to author
Forward
0 new messages