I’m working on a map application where I place multiple markers based on location data. When I click on any marker, I want to show the specific address for that marker in a popup window. Right now, clicking any marker always shows the address from the last item in my data array instead of the clicked marker’s address.
var pins = [];
var location;
var i;
for (i = 0; i < locationData.length; i++) {
location = {
lat: parseFloat(locationData[i].latitude),
lng: parseFloat(locationData[i].longitude)
};
var addressText = locationData[i].streetAddress;
var mapPin = new google.maps.Marker({
position: location,
map: myMap,
icon: pinIcon,
title: addressText
});
pins.push(mapPin);
var popup = new google.maps.InfoWindow();
google.maps.event.addListener(mapPin, 'click', function() {
popup.setContent(addressText);
popup.open(myMap, mapPin);
});
}
The problem is that every marker shows the same address when clicked. How can I fix this so each marker displays its own correct address information when clicked?
classic closure problem! your addressText gets overwritten every loop. wrap the event listener in an IIFE to capture the current value: (function(addr) { google.maps.event.addListener(mapPin, 'click', function() { popup.setContent(addr); popup.open(myMap, mapPin); }); })(addressText); this gives each marker its own scope.
You’ve got a classic variable scoping issue - all your event listeners are pointing to the same addressText variable after the loop finishes. Easy fix: bind the data directly to each marker using the set() method. Just store locationData[i].streetAddress as a custom property with mapPin.set('address', locationData[i].streetAddress), then in your click listener use popup.setContent(this.get('address')) instead of that loop variable. This kills the closure problem completely since each marker owns its data. I’ve done this in tons of mapping projects and it beats IIFE solutions, especially when you’re storing multiple properties per marker.
Had this exact issue building a restaurant locator last year. The closure trap gets everyone eventually. What works really well is using bind() on your event handler function. Instead of fighting closures or creating extra marker properties, just bind the current addressText value directly to the callback. Change your event listener to google.maps.event.addListener(mapPin, 'click', function(address) { popup.setContent(address); popup.open(myMap, this); }.bind(null, addressText)); The bind creates a new function with the addressText value locked in at creation time, so each marker gets its own version. Clean solution that doesn’t require much restructuring of your existing code.
The Problem:
You are encountering an issue where clicking on map markers in your application always displays the address from the last item in your location data array, regardless of which marker is clicked. This is because of a classic JavaScript closure problem in your event listener.
Understanding the “Why” (The Root Cause):
The problem lies in how JavaScript handles closures within loops. Your code iterates through locationData, creating a marker and an event listener for each. The event listener function, however, doesn’t capture the addressText value at the time of creation. Instead, it uses the final value of addressText which is the last item’s address once the loop completes. This is why each marker’s click shows the same address. To fix this, each marker’s event listener needs its own private copy of the addressText value from its iteration.
Step-by-Step Guide:
-
Automate Marker Creation and Event Handling: Instead of manually creating markers and attaching event listeners in a loop, automate this process. This approach eliminates the closure problem entirely, ensuring that each marker is correctly associated with its data. This can be achieved through a function that takes location data as input, dynamically generates the markers, and attaches event listeners that directly access the marker’s data.
Here is an example using a function to encapsulate marker creation:
function createMarkerWithAddress(map, locationData) {
locationData.forEach(locationItem => {
const location = {
lat: parseFloat(locationItem.latitude),
lng: parseFloat(locationItem.longitude)
};
const addressText = locationItem.streetAddress;
const mapPin = new google.maps.Marker({
position: location,
map: map,
icon: pinIcon,
title: addressText,
address: addressText // Store address directly on the marker
});
const popup = new google.maps.InfoWindow();
google.maps.event.addListener(mapPin, 'click', function() {
popup.setContent(this.address); // Access address from the marker object
popup.open(map, mapPin);
});
});
}
createMarkerWithAddress(myMap, locationData);
This revised code creates a new function that iterates over the locationData. It then creates a new Marker for each location. Crucially, the address is stored as a property on the marker itself (mapPin.address = addressText). The click listener retrieves the address directly from the marker instance using this.address, solving the closure problem.
-
Verify Your Data: Ensure your locationData array is correctly formatted, with latitude, longitude, and streetAddress properties for each location. Inspect the data structure using your browser’s developer tools to confirm this.
-
Check Map Initialization: Verify that myMap is properly initialized and available when createMarkerWithAddress is called.
Common Pitfalls & What to Check Next:
- Incorrect Data: Double-check the format of your
locationData. Missing or incorrectly formatted address data will prevent correct marker display.
- Asynchronous Operations: If your
locationData is fetched asynchronously (e.g., from an API), ensure the markers are created only after the data has fully loaded. Use promises or async/await to handle this.
- Map API Errors: Make sure your Google Maps API key is valid and that the map is correctly initialized. Check the Google Maps JavaScript API documentation for troubleshooting.
Still running into issues? Share your (sanitized) config files, the exact command you ran, and any other relevant details. The community is here to help!
This topic was automatically closed 6 hours after the last reply. New replies are no longer allowed.