Basically the problem is in here:
for (i = 0; i<locations.length; i++){
var marker = new google.maps.Marker({
position: locations[i],
draggable: true,
map: map,
});
markers.push(marker);
google.maps.event.addListener(markers[i], 'mouseover', function() {
markers[i].setIcon(gif);
});
google.maps.event.addListener(markers[i], 'mouseout', function() {
markers[i].setIcon(null);
});
};
This code looks innocent, but the problem comes from the part that, once the loop has finished executing and our event listeners are called, the variable i
is already equal to locations.length
.
So whenever the event listener is called, i
is already changed to locations.length
, and markers[i]
will return undefined, because the last push index was i = locations.length - 1
since the loop condition is i<locations.length
.
Since markers[i]
is undefined when the event listener is called, then it will throw the following error as it doesn't have setIcon
method anymore: TypeError: Cannot read property 'setIcon' of undefined
.
To fix this, you should capture value of i
, in a closure(as Anto Jurković described above, don't forget to upvote him):
(function(i) {
google.maps.event.addListener(markers[i], 'mouseover', function() {
markers[i].setIcon(gif);
});
google.maps.event.addListener(markers[i], 'mouseout', function() {
markers[i].setIcon(null);
});
})(i);
Here we create a function on each loop and call it with i
immediately so the value of i
is captured in the closure, and since javascript functions have block scope(loops don't), the variable i
will be the same for each iteration of loop.
The problem and it's solution is also described in the following question: Javascript closure inside loops - simple practical example