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?

2 Answers 2

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)

Sign up to request clarification or add additional context in comments.

1 Comment

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.
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 :-)

3 Comments

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?
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.
Thanks. I've sorted it by using an anonymous function instead, and passing the variable as a parameter of that function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.