简体   繁体   中英

Google Maps API v3 adding an InfoWindow to each marker

NOTE: I'm using v3 of the Google Maps API

I'm trying to add an info window to each marker I put on the map. Currently I'm doing this with the following code:

for (var i in tracks[racer_id].data.points) {
    values = tracks[racer_id].data.points[i];                
    point = new google.maps.LatLng(values.lat, values.lng);
    if (values.qst) {
        var marker = new google.maps.Marker({map: map, position: point, clickable: true});
        tracks[racer_id].markers[i] = marker;
        var info = new google.maps.InfoWindow({
            content: '<b>Speed:</b> ' + values.inst + ' knots'
        });
        tracks[racer_id].info[i] = info;
        google.maps.event.addListener(marker, 'click', function() {
            info.open(map, marker);
        });
    }
    track_coordinates.push(point);
    bd.extend(point);
}

The problem is when I click on a marker it just displays the info window for the last marker added. Also just to be clear the info window appears next to the last marker not the marker clicked on. I'd imagine my problem is in the addListener portion but am not postitive. Any ideas?

You are having a very common closure problem in the for in loop:

Variables enclosed in a closure share the same single environment, so by the time the click callback from the addListener is called, the loop will have run its course and the info variable will be left pointing to the last object, which happens to be the last InfoWindow created.

In this case, one easy way to solve this problem would be to augment your Marker object with the InfoWindow :

var marker = new google.maps.Marker({map: map, position: point, clickable: true});

marker.info = new google.maps.InfoWindow({
  content: '<b>Speed:</b> ' + values.inst + ' knots'
});

google.maps.event.addListener(marker, 'click', function() {
  marker.info.open(map, marker);
});

This can be quite a tricky topic, if you are not familiar with how closures work. You may to check out the following Mozilla article for a brief introduction:

Also keep in mind, that the v3 API allows multiple InfoWindow s on the map. If you intend to have just one InfoWindow visible at the time, you should instead use a single InfoWindow object, and then open it and change its content whenever the marker is clicked ( Source ).

You can use this in event:

google.maps.event.addListener(marker, 'click', function() {  
    // this = marker
    var marker_map = this.getMap();
    this.info.open(marker_map);
    // this.info.open(marker_map, this);
    // Note: If you call open() without passing a marker, the InfoWindow will use the position specified upon construction through the InfoWindowOptions object literal.
});

The add_marker still has a closure issue, cause it uses the marker variable outside the google.maps.event.addListener scope.

A better implementation would be:

function add_marker(racer_id, point, note) {
    var marker = new google.maps.Marker({map: map, position: point, clickable: true});
    marker.note = note;
    google.maps.event.addListener(marker, 'click', function() {
        info_window.content = this.note;
        info_window.open(this.getMap(), this);
    });
    return marker;
}

I also used the map from the marker, this way you don't need to pass the google map object, you probably want to use the map where the marker belongs to anyway.

for Earth plugin APIs, create the balloon outside your loop and pass your counter to the function to get unique contents for each placemark!

function createBalloon(placemark, i, event) {
            var p = placemark;
            var j = i;
            google.earth.addEventListener(p, 'click', function (event) {
                    // prevent the default balloon from popping up
                    event.preventDefault();
                    var balloon = ge.createHtmlStringBalloon('');
                    balloon.setFeature(event.getTarget());

                    balloon.setContentString('iframePath#' + j);

                    ge.setBalloon(balloon);
            });
        }

Hey everyone. I don't know if this is the optimal solution but I figured I'd post it here to hopefully help people out in the future. Please comment if you see anything that should be changed.

My for loops is now:

for (var i in tracks[racer_id].data.points) {
    values = tracks[racer_id].data.points[i];                
    point = new google.maps.LatLng(values.lat, values.lng);
    if (values.qst) {
        tracks[racer_id].markers[i] = add_marker(racer_id, point, '<b>Speed:</b> ' + values.inst + ' knots<br /><b>Invalid:</b> <input type="button" value="Yes" /> <input type="button" value="No" />');
    }
    track_coordinates.push(point);
    bd.extend(point);
}

And add_marker is defined as:

var info_window = new google.maps.InfoWindow({content: ''});

function add_marker(racer_id, point, note) {
    var marker = new google.maps.Marker({map: map, position: point, clickable: true});
    marker.note = note;
    google.maps.event.addListener(marker, 'click', function() {
        info_window.content = marker.note;
        info_window.open(map, marker);
    });
    return marker;
}

You can use info_window.close() to turn off the info_window at any time. Hope this helps someone.

In My case (Using Javascript insidde Razor) This worked perfectly inside an Foreach loop

google.maps.event.addListener(marker, 'click', function() {
    marker.info.open(map, this);
});

The only way I could finally get this to work was by creating an array in JavaScript. The array elements reference the various info-windows (one info-window is created for each marker on the map). Each array element contains the unique text for its appropriate map marker. I defined the JavaScript event for each info-window based on the array element. And when the event fires, I use the "this" keyword to reference the array element to reference the appropriate value to display.

var map = new google.maps.Map(document.getElementById('map-div'), mapOptions);
zipcircle = [];
for (var zip in zipmap) {
    var circleoptions = {
        strokeOpacity: 0.8,
        strokeWeight: 1,
        fillOpacity: 0.35,
        map: map,
        center: zipmap[zip].center,
        radius: 100
    };
    zipcircle[zipmap[zip].zipkey] = new google.maps.Circle(circleoptions);
    zipcircle[zipmap[zip].zipkey].infowindowtext = zipmap[zip].popuptext;
    zipcircle[zipmap[zip].zipkey].infowindow = new google.maps.InfoWindow();
    google.maps.event.addListener(zipcircle[zipmap[zip].zipkey], 'click', function() {
        this.infowindow.setContent(this.infowindowtext);
        this.infowindow.open(map, this);
    });
}

I had a similar problem. If all you want is for some info to be displayed when you hover over a marker, instead of clicking it, then I found that a good alternative to using an info Window was to set a title on the marker. That way whenever you hover the mouse over the marker the title displays like an ALT tag. 'marker.setTitle('Marker '+id);' It removes the need to create a listener for the marker too

Try this:

for (var i in tracks[racer_id].data.points) {
    values = tracks[racer_id].data.points[i];                
    point = new google.maps.LatLng(values.lat, values.lng);
    if (values.qst) {
        var marker = new google.maps.Marker({map: map, position: point, clickable: true});
        tracks[racer_id].markers[i] = marker;
        var info = new google.maps.InfoWindow({
            content: '<b>Speed:</b> ' + values.inst + ' knots'
        });
        tracks[racer_id].info[i] = info;
        google.maps.event.addListener(tracks[racer_id].markers[i], 'click', function() {
            tracks[racer_id].info[i].open(map, tracks[racer_id].markers[i]);
        });
    }
    track_coordinates.push(point);
    bd.extend(point);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM