O Google Maps v3 abre a última infoWindow quando um marcador é clicado

Eu tenho uma infoWindow compartilhada para todos os meus marcadores. Ele funciona bem se eu usar o $().each(function(){}) jquery $().each(function(){}) , mas se eu alterá-lo para o loop nativo ou while do JavaScrips, ele não está funcionando como esperado.

Sempre que eu clico em um marcador, ele abre a infoWindow do último marcador preenchido, em vez da infoWindow do marcador clicado.

Isso funciona:

 $(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 content var infoWindowContent = '
' + '' + city + ' #' + storeId + ' (' + parseFloat(distance).toFixed(2) + 'mi)
' + address + '
' + city + ', ' + state + ' ' + zip + '
'; google.maps.event.addListener(marker, 'click', function() { InStorePickup.openInfoWindow(marker, infoWindowContent); }); bounds.extend(point); });

Mas isso não funciona:

 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 = '
' + '' + city + ' #' + storeId + ' (' + parseFloat(distance).toFixed(2) + 'mi)
' + address + '
' + city + ', ' + state + ' ' + zip + '
'; google.maps.event.addListener(marker, 'click', function() { InStorePickup.openInfoWindow(marker, infoWindowContent); }); bounds.extend(point); }

Alguma ideia?

Você estaria tendo um problema de fechamento muito comum no loop for . Aliás, eu respondi uma pergunta semelhante sobre esse assunto ontem, que você pode querer verificar.

As variables ​​incluídas em um encerramento compartilham o mesmo ambiente único, portanto, no momento em que o retorno de chamada de click for chamado, o loop terá executado seu curso e a variável infoWindowContent será deixada apontando para o último valor ao qual foi atribuído.

Uma maneira de resolver isso é com mais closures, usando uma fábrica de funções:

 function makeInfoWindowListener (pMarker, pContent) { return function() { InStorePickup.openInfoWindow(pMarker, pContent); }; } // ... for (var i = 0; i < 3; i++) { // ... var infoWindowContent = '
' + city + '...
'; google.maps.event.addListener( marker, 'click', makeInfoWindowListener(marker, infoWindowContent) ); }

Esse pode ser um assunto bastante complicado, se você não estiver familiarizado com o funcionamento dos encerramentos. Você pode estar interessado em conferir o seguinte artigo para uma breve introdução:

  • Mozilla Dev Center: trabalhando com fechamentos

ATUALIZAR:

A razão pela qual o exemplo jQuery funciona, é porque a function que você passa para o método each() inclui cada iteração em seu próprio escopo. Você poderia praticamente fazer a mesma coisa em seu exemplo simples de JavaScript:

 for (var i = 0; i < 3; i++) { // each iteration will now have its own scope (function () { var infoWindowContent = '
' + city + '...
'; google.maps.event.addListener(marker, 'click', function() { InStorePickup.openInfoWindow(marker, infoWindowContent); }); })(); }

2a ATUALIZAÇÃO:

Observe que o primeiro exemplo também pode ser reescrito da seguinte maneira, usando uma function anônima:

 for (var i = 0; i < 3; i++) { // ... var infoWindowContent = '
' + city + '...
'; google.maps.event.addListener(marker, 'click', (function (pMarker, pContent) { return function() { InStorePickup.openInfoWindow(pMarker, pContent); }; })(marker, infoWindowContent)); }
    Intereting Posts