I’m trying to pass an array into a function and then use the information in that array to initialize a google map. However, when I click on a marker on the map, an error is produced which says:
Unable to get value of the property ‘popupHtml’: object is null or
undefined
The reason for creating the function is so that the javascript code can be moved to a seperate .js file, thus becoming seperate from the html file. Is there anyway this problem can be corrected? Here’s all my code (I put a comment in to mark where the error is occuring…):
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
window.onload = function ()
{
function loadMap(markers)
{
var options =
{
center: new google.maps.LatLng(40.775813, -73.970786),
zoom: 17,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById('map'), options);
var points = new Array();
for (var i = 0; i < markers.length; i++)
points.push(new google.maps.LatLng(markers[i].lat, markers[i].lon));
var infoWindow = new google.maps.InfoWindow();
for (var i = 0; i < markers.length; i++)
{
var googleMarker = new google.maps.Marker({ position: points[i], map: map, title: markers[i].title });
google.maps.event.addListener(
googleMarker,
'click',
function ()
{
// The following line is where the error is occuring:
infoWindow.setContent(markers[i].popupHtml);
infoWindow.open(map, googleMarker);
});
}
};
loadMap([{
"lat": "40.776512",
"lon": "-73.970293",
"popupHtml": "\u003cdiv\u003eHello world - from marker 1!\u003c/div\u003e",
"title": "Marker 1!"
},
{
"lat": "40.774659",
"lon": "-73.971548",
"popupHtml": "\u003cdiv\u003eHello world - from marker 2!\u003c/div\u003e",
"title": "Marker 2!"
}]);
};
</script>
</head>
<body>
<div id="map" style="width: 680px; height: 400px;"></div>
</body>
</html>
The problem is that the function you’re passing into
addListenerhas enduring access to theivariable, not a copy of its value as of when the function was created. So all of the copies of the function seeias of when they’re called, which is presumably past the end of the array. The same goes forgoogleMarker; they’ll all see the last value it had in the loop rather than the current value.You fix it by using a generator function or (if you can rely on ECMAScript5, or if you use an ES5 shim since
bindis something a shim can provide) useFunction#bind.Using
bind:bindreturns a function that, when called, will call the original function with a giventhisvalue and the arguments you give it. So in the above, we callbindpassing in the value ofiandgoogleMarker, and it gives us a function that, when called, will call our original with those value as arguments. Then we use the arguments (indexandthisMarker) instead ofiandgoogleMarker.If you can’t rely on ES5 features in your target browsers and you don’t want to use a shim, you can use a generator function:
There we’re calling
makeHandler, passing in the value ofiandgoogleMarker, andmakeHandlerreturns us a function that closes over those arguments (indexandthisMarker) rather thaniandgoogleMarker. Since themakeHandlerarguments won’t change, our function will see the correct values.This all has to do with how closures work. More about closures: Closures are not complicated