2

I have a website where users can search for professional wrestling events in the United Kingdom by entering there location. The site uses the Google Maps API and a bespoke API to query the database and return the events via AJAX.

The JavaScript function I'm using to iterate over results from my API looks like this:

function setMarkers(map, events) {
    var geocoder = new google.maps.Geocoder();
    for (var i=0; i < events.length; i++) {
        var wrestling_event = events[i];
        console.log(wrestling_event);
        var image_a = '/images/marker.png';
        var image_b = '/images/marker-over.png';
        geocoder.geocode({ address: wrestling_event.venue.post_code }, function(results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
                var marker = new google.maps.Marker({
                    position: results[0].geometry.location,
                    map: map,
                    icon: image_a
                });
                var infowindow = new google.maps.InfoWindow({
                    content: '<p><strong>' + wrestling_event.date + ': ' + wrestling_event.name + '</strong><br />' +
                             wrestling_event.venue.name + ',<br />' +
                             wrestling_event.venue.street_address + ',<br />' +
                             wrestling_event.venue.city + ',<br />' +
                             wrestling_event.venue.post_code + '</p>'
                });
                google.maps.event.addListener(marker, 'mouseover', function() {
                    marker.setIcon(image_b);
                });
                google.maps.event.addListener(marker, 'mouseout', function() {
                    marker.setIcon(image_a);
                });
                google.maps.event.addListener(marker, 'click', function() {
                    infowindow.open(map, marker);
                });
            }
        });
    }
};

This is adapted from the Google Maps API sample, found here: http://code.google.com/apis/maps/documentation/javascript/examples/icon-complex.html

The issue I'm having is, if I have multiple results returned from the bespoke API, my JavaScript function is printing the same event over and over, despite logging the result of the wrestling_event object.

The wrestling_event variable seems to get overwritten (or not after the first iteration in the for() loop) inside the geocoding block. Is there a reason for this?

Martin Bean
  • 38,379
  • 25
  • 128
  • 201

2 Answers2

3

See my answer How to pass parameter to an anonymous function defined in the setTimeout call?

Short answer: it's because the call to the geocoder is asynchronous (like setTimeout)

Community
  • 1
  • 1
Halcyon
  • 57,230
  • 10
  • 89
  • 128
  • You can in fact do this with a separate function, and you can do it with ".bind()" in a way that's a little cleaner than typing in an anonymous "lambda" function. – Pointy Jun 20 '11 at 20:53
1

Yes, there's a reason - all the callback functions constructed in the loop share the same variable.

Code blocks in loops do not create a new scope in the way they do in languages like Java or C#. Only functions create new scopes.

What you could do is write a separate function that returns the handler. You'd pass that "wrestling_event" into the function, and it can return the callback code you've got. You'd then pass that return value in as the callback parameter to the "geocode()" routine.

This is such a common pitfall in JavaScript that it should be described on the back cover of every book on the language :-)

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • So would it be better to create a function that creates only one marker and attached infowindow, and then call that function with each pass of my `for()` loop? – Martin Bean Jun 20 '11 at 20:51
  • Yes, you could do it that way too. The key is to *somehow* make a copy of that "wrestling_event" value that can be retained privately by each callback function. If you pass it into a separate function and do the "geocode()" call there, that'll work fine. – Pointy Jun 20 '11 at 20:56
  • Thanks. I've sorted it by using an anonymous function instead, and passing the variable as a parameter of that function. – Martin Bean Jun 20 '11 at 21:01