32

I'm currently using the following code the place multiple markers on a Google Map using their API.

The problem I'm having is with multiple infowindows not working (only showing the last one).

There are loads of questions like mine here on SO. Actually make that a shitload of questions :-)

Just an example: Trying to bind multiple InfoWindows to multiple Markers on a Google Map and failing

The solution to my problem is kinda easy: just enclose the click listener to a (anonymous) function.

However what I don't understand is why my solution isn't working (saving the markers and infowindows in arrays instead of just one variable).

    var markers = [];
    var infowindows = [];

    // add shops or malls
    for (var key in data.markers) {
      if (data.markers.hasOwnProperty(key)) {
        infowindows[key] = new google.maps.InfoWindow({
            content: data.markers[key].infowindow
        });

        markers[key] = new google.maps.Marker({
                position: new google.maps.LatLng(data.markers[key].location.lat, data.markers[key].location.lng),
                map: map,
                flat: true,
                title: data.markers[key].name,
                draggable: false
        });
        var iconFile = 'http://maps.google.com/mapfiles/ms/icons/'+marker_color+'-dot.png';
        markers[key].setIcon(iconFile);

        google.maps.event.addListener(markers[key], 'click', function() {
          infowindows[key].open(map, markers[key]);
        });
      }
    }

So... I don't what to get the solution how to get it working with some function to enclose the listener (although it should work, haven't tested it yet but will), but I want to know the reason why it wouldn't work if I add the markers and infowindows to arrays instead.

Community
  • 1
  • 1
PeeHaa
  • 71,436
  • 58
  • 190
  • 262

6 Answers6

62

Javascript has a language structure called "closures". Closures are functions (such as the function() {} you declare above to deal with click listener) which capture references to external variables.

There are plenty of resources which explain them better than I can, which I suggest you consult, but here's my best attempt:

In this block here:

    google.maps.event.addListener(markers[key], 'click', function() {
      infowindows[key].open(map, markers[key]);
    });

Because "key" is already defined as an external variable, the function will capture a reference to that variable. So where you expect:

infowindows["helloworld"]

Javascript will instead interpret this as:

infowindows[reference to key]

When you click on a marker, it looks up "reference to key" to see what the current value of key is. Because this probably won't happen until your loop has finished, key will be equal to whatever the last key in your data.markers object is. And it will be equal to that value for EVERY click listener you added.

The solution, as you point out, is to wrap this in an anonymous function to get Javascript to evaluate the value of "key" at the time that the click listener is added.

  google.maps.event.addListener(markers[key], 'click', function(innerKey) {
      return function() {
          infowindows[innerKey].open(map, markers[innerKey]);
      }
    }(key));
OptimusCrime
  • 14,662
  • 13
  • 58
  • 96
plexer
  • 4,542
  • 2
  • 23
  • 27
54

This works fine for me! Just add a new property to marker object, this property contains the infowindow object.

var mytext = 'Infowindow contents in HTML'
var myinfowindow = new google.maps.InfoWindow({
    content: mytext
});

var marker = new google.maps.Marker({
    position: mypos,
    map: mymap,
    icon: myicon,
    title: mytitle,
    infowindow: myinfowindow
});

google.maps.event.addListener(marker, 'click', function() {
        this.infowindow.open(map, this);

});
Tekbreak
  • 686
  • 5
  • 5
  • 5
    This does not answer the original question at all! It just shows the example from the Google Maps API reference with ONE info window. Sorry, but this answer should not be here. – Zordid Oct 19 '15 at 07:54
20

There is a slightly simpler way to accomplish this. you can add a custom attribute to your marker (the info window's index) and refer to it in the callback function. Example below:

    markers = Array();
    infoWindows = Array();

    for(var i in earthquakes)
    {
        var location = new google.maps.LatLng(earthquakes[i].geolat, earthquakes[i].geolong);
        var marker = new google.maps.Marker({
            position : location,
            map : map,
            animation : google.maps.Animation.DROP,
            infoWindowIndex : i //<---Thats the extra attribute
        });
        var content = "<h3>" + earthquakes[i].title + "<h3>" +
                "<a data-ajax='false' target='_blank' href='" + earthquakes[i].link + "'>Link to shakemap.</a>";
        var infoWindow = new google.maps.InfoWindow({
            content : content
        });

        google.maps.event.addListener(marker, 'click', 
            function(event)
            {
                map.panTo(event.latLng);
                map.setZoom(5);
                infoWindows[this.infoWindowIndex].open(map, this);
            }
        );

        infoWindows.push(infoWindow);
        markers.push(marker);
    }
  • I'm trying this way, but I have a jshint warning "Don't make functions within a loop". If I take the listener outside de loop I get "marker used out of scope" error. How can I avoid it? – aitor May 01 '16 at 15:30
2
    function addMarker(Latlng){ 
       marker = new google.maps.Marker({
            position: Latlng,
            icon:"myicon.jpg",
            map: map,
            animation: google.maps.Animation.DROP,
            title:"My tooltip"
        });

        //add marker
        marker.setMap(map);

        contentString = "<h1>Hello World</h1>";

        marker.infowindow = new google.maps.InfoWindow({
            content: contentString
        });

        //add click event
        google.maps.event.addListener(marker, 'click', function(){
            this.infowindow.open(map,this);
        });
    }

    addMarker(some_lat_lang_value);
sidarcy
  • 2,958
  • 2
  • 36
  • 37
1

i will use closure:

(function(infowindow, marker){google.maps.event.addListener(marker, 'click', function() {
  infowindow.open(map, marker);
});})(infowindow, marker)
Sagiv Ofek
  • 25,190
  • 8
  • 60
  • 55
0
var infowindow = null;
infowindow = new google.maps.InfoWindow({
  content: "loading..."
});
for (i = 0; i < data.dbreturn.length; i++) { 
  var latilongi = data.dbreturn[i].mapa.split(',');
  var mapinha =  {lat: parseFloat(latilongi['0']), lng:     parseFloat(latilongi['1'])};
  var marker = new google.maps.Marker({
  position: mapinha,
  html: '<div id="content"><h5 id="firstHeading"     class="firstHeading">'+data.dbreturn[i].nome+'</h5></div>'
});

google.maps.event.addListener(marker, "click", function () {
  infowindow.setContent(this.html);
  infowindow.open(map, this);
});
}
  • 2
    Welcome to Stack Overflow! While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value. – Ajean May 16 '16 at 21:47