Google Maps v3 opens the last infoWindow when a marker is clicked
I have a shared infoWindow
for all my markers. It works good if I use jquery's $().each(function(){})
, but if I change it to JavaScrips's native for or while loop, it's not working as expected.
Whenever I click a marker, it will open the last populated marker's infoWindow
, instead of the clicked marker's infoWindow
.
This works:
$(stores).each(function() {
var storeId = $(this).attr('storeId');
var address = $(this).attr('rawAddress');
var city = $(this).attr('city');
var state = $(this).attr('state');
var zip = $(this).attr('zip');
var lat = $(this).attr('lat');
var lng = $(this).attr('lng');
var distance = $(this).attr('distance');
//create marker
var point = new google.maps.LatLng(lat, lng);
var marker = new google.maps.Marker({ position: point
, map: InStorePickup.map
, title: city + ' #' + storeId
});
//create infowindow c开发者_Go百科ontent
var infoWindowContent = '<div style="font-size: 8pt;">'
+ '<strong>' + city + ' #' + storeId + '</strong> (' + parseFloat(distance).toFixed(2) + 'mi)<br />'
+ address + '<br />' + city + ', ' + state + ' ' + zip
+ '</div>';
google.maps.event.addListener(marker, 'click', function() {
InStorePickup.openInfoWindow(marker, infoWindowContent);
});
bounds.extend(point);
});
But this does not work:
for (var i = 0; i < 3; i++) {
var store = stores[i];
var storeId = $(store).attr('storeId');
var address = $(store).attr('rawAddress');
var city = $(store).attr('city');
var state = $(store).attr('state');
var zip = $(store).attr('zip');
var lat = $(store).attr('lat');
var lng = $(store).attr('lng');
var distance = $(this).attr('distance');
//create marker
var point = new google.maps.LatLng(lat, lng);
var marker = new google.maps.Marker({ position: point
, map: InStorePickup.map
, title: city + ' #' + storeId
});
//create infowindow content
var infoWindowContent = '<div style="font-size: 8pt;">'
+ '<strong>' + city + ' #' + storeId + '</strong> (' + parseFloat(distance).toFixed(2) + 'mi)<br />'
+ address + '<br />' + city + ', ' + state + ' ' + zip
+ '</div>';
google.maps.event.addListener(marker, 'click', function() {
InStorePickup.openInfoWindow(marker, infoWindowContent);
});
bounds.extend(point);
}
Any thoughts?
You would be having a very common closure problem in the for
loop. Incidentally I have answered a similar question on this topic just yesterday, which you may want to check out.
Variables enclosed in a closure share the same single environment, so by the time the click
callback is called, the loop will have run its course and the infoWindowContent
variable will be left pointing to the last value it was assigned to.
One way to solve this is with more closures, using a function factory:
function makeInfoWindowListener (pMarker, pContent) {
return function() {
InStorePickup.openInfoWindow(pMarker, pContent);
};
}
// ...
for (var i = 0; i < 3; i++) {
// ...
var infoWindowContent = '<div style="font-size: 8pt;">' + city + '...</div>';
google.maps.event.addListener(
marker,
'click',
makeInfoWindowListener(marker, infoWindowContent)
);
}
This can be quite a tricky topic, if you are not familiar with how closures work. You may be interested in checking out the following article for a brief introduction:
- Mozilla Dev Center: Working with Closures
UPDATE:
The reason why the jQuery example works, is because the function you pass to the each()
method encloses each iteration in its own scope. You could practically do the same thing in your plain JavaScript example:
for (var i = 0; i < 3; i++) {
// each iteration will now have its own scope
(function () {
var infoWindowContent = '<div style="font-size: 8pt;">' + city + '...</div>';
google.maps.event.addListener(marker, 'click', function() {
InStorePickup.openInfoWindow(marker, infoWindowContent);
});
})();
}
2nd UPDATE:
Note that the first example can also be rewritten as follows, using an anonymous function:
for (var i = 0; i < 3; i++) {
// ...
var infoWindowContent = '<div style="font-size: 8pt;">' + city + '...</div>';
google.maps.event.addListener(marker, 'click', (function (pMarker, pContent) {
return function() {
InStorePickup.openInfoWindow(pMarker, pContent);
};
})(marker, infoWindowContent));
}
精彩评论