1

I'm trying to make a map-based application, but I'm having bit of difficulty. The original code I had been working with added a separate InfoWindow for each marker, but I'd like to get it using a single InfoWindow for which I can set the content on the fly.

However, I still seem to be a little fuzzy on how JavaScript behaves, because each time any marker is clicked the InfoWindow pops up over the last marker and the alert indicates the ID of the last entry in locations.

Short snippet, problem highlighted:

function plotLocations(my_locations) {
    locations = my_locations;
    for(var i=0; i<locations.length; i++) {
        var pos = new google.maps.LatLng(locations[i].loc_lat, locations[i].loc_lng);
        var icon = new google.maps.MarkerImage(
            "http://goo.gl/TQpwU",
            new google.maps.Size(20,32),
            new google.maps.Point(0,0),
            new google.maps.Point(0,32)
        );
        var marker = new google.maps.Marker({
            map: map,
            position: pos,
            animation: google.maps.Animation.DROP,
            icon: icon
        });
// ! -- trouble right here -- ! //
        google.maps.event.addListener(marker, 'click', function() {
            setPopInfo(pos, i);
        });
// ! -- ------------------ -- ! //
    }
}

function setPopInfo(pos, index) {
    pop_info.setPosition(pos);
    pop_info.open(map);
    window.alert(pos+"::"+index);
}

Most of the rest of my code:

var map;
var mapBounds;
var locations;
var pop_info;

$(document).ready(init);

function init() {
    var mapOptions = {
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions);
    pop_info = new google.maps.InfoWindow({
        content: "I'm not populated!",
        size: new google.maps.Size(100,25)
    });
    google.maps.event.addListener(map, 'bounds_changed', function() {
        queryLocations(map.getBounds());
    });

    prepareGeolocation();
    doGeolocation();
}

function queryLocations(bounds) {
    jQuery.ajax({
        url: 'http://mydomain.com/myapp/test2.php',
        data: bounds.toString(),
        dataType: 'json',
        success: addLocations
    });
}

function addLocations(new_locations) {
    document.getElementById('footer').innerHTML = new_locations;
    plotLocations(new_locations);
}

My reasoning for the single InfoWindow is that once a few hundred Markers and InfoWindows have been created the performance might take a nosedive. Is what I'm trying to do feasible/advisable?

Sammitch
  • 30,782
  • 7
  • 50
  • 77
  • possible duplicate of [Multiple Google Maps infowindow](http://stackoverflow.com/questions/4381355/multiple-google-maps-infowindow) – geocodezip Jan 15 '13 at 00:15
  • No, premature optimization is pointless, especially if it breaks your code! – Fraser Jan 15 '13 at 05:58
  • @Fraser what if I call it a "design decision"? :D – Sammitch Jan 15 '13 at 15:48
  • @Sammitch I'd laugh and say it is a bad design decision - http://stackoverflow.com/questions/385506/when-is-optimisation-premature – Fraser Jan 15 '13 at 21:08
  • @Fraser but I'm not trying to shave off 3 CPU cycles by finding a "more efficient" loop/logic/string/etc construct, I'm avoiding the creation of dozens/hundreds of unnecessary objects which may eventually contain several kilobytes of data. My inexperience with Javascript aside, I know shit code when I see it and the code that I fixed *was shit* and would have been problematic in the future. – Sammitch Jan 15 '13 at 21:17
  • @Sammitch with all that surety one wonders why you asked if it was advisable. The basic point is that is a bad idea to try to optimize any system before that optimization it is *required*. You are trying to fix a *perceived* problem that doesn't yet exist - and that is generally a bad idea. In any case. you asked for advice and I gave it to you... – Fraser Jan 15 '13 at 23:02
  • possible duplicate of [Closing any open info windows in google maps api v3](http://stackoverflow.com/questions/2966744/closing-any-open-info-windows-in-google-maps-api-v3) – Fraser Jan 15 '13 at 23:07

1 Answers1

7

The problem occurs because you're defining your event listener callback function inside the loop. Closures refer to their context dynamically rather than binding to a copy of the data at the time of definition, so you have effectively created locations.length number of functions that are all bound to the same values of marker, pos and i, which are whatever they happened to be when the loop terminated.

To work round this, you could create a function that calls addListener outside the body of the loop, like so:

function plotLocations (my_locations) {
    locations = my_locations;
    for(var i=0; i<locations.length; i++) {
        var pos = new google.maps.LatLng(locations[i].loc_lat, locations[i].loc_lng);
        var icon = new google.maps.MarkerImage(
            "http://goo.gl/TQpwU",
            new google.maps.Size(20,32),
            new google.maps.Point(0,0),
            new google.maps.Point(0,32)
        );
        var marker = new google.maps.Marker({
            map: map,
            position: pos,
            animation: google.maps.Animation.DROP,
            icon: icon
        });
        bindEvent(marker, pos, i);
    }
}

function bindEvent (marker, pos, i) {
    google.maps.event.addListener(marker, 'click', function() {
        setPopInfo(pos, i);
    });
}
Phil Booth
  • 4,853
  • 1
  • 33
  • 35