I'm trying to add markers to a Google maps using V3. I have my places into an json object (places):
var num_places = places.length;
for(var i = 0; i < num_places; i++)
{
place_lat_lng = new google.maps.LatLng(places[i].lat, places[i].lng);
var infowindow = new google.maps.InfoWindow({
content: '<h2>' + places[i].name + '</h2><p>' + places[i].body + '</p>'
});
var marker = new google.maps.Marker({
position: place_lat_lng,
map: mymap,
title: places[i].name,
zIndex: i
});
google.maps.event.addListener(marker, 'click', function() {
infowindow.open(mymap, marker);
});
}
The code insert the markers, but when I click on any of them the infoWindow show (and move the map) always to the last marker in the list.
I tryed to use an array to for the infoWindow:
var infoWindow = new Array();
for(var i = 0; i < num_places; i++)
{
[...]
var infowindow[i] = new google.maps.InfoWindow({
content: '<h2>' + places[i].name + '</h2><p>' + places[i].body + '</p>'
});
[...]
google.maps.event.addListener(marker, 'click', function() {
infowindow[i].open(mymap, marker);
});
}
But nothings change.
Where am I mistaking?
In JavaScript, only functions create a new scope/closure. Therefore you only ever have one infowindow variable, which is captured into the inner function, but which in the end will point to the last window. Use a self-calling function to set up a new closuse:
for (var i = 0; ...) {
(function() {
var infowindow = ...;
})();
}
Note that the value of i will still not be captured separately. Currently you don't seem to need it, but if you do, pass it to the function to create a local version of it inside the function:
(function(i) { // this is the new local i with the value passed in from the outside
// ...accessible here
})(i); // this is the outer i
Change this:
google.maps.event.addListener(marker, 'click', function() {
infowindow.open(mymap, marker);
});
to:
google.maps.event.addListener(marker, 'click', function(infowindow) {
function() {
infowindow.open(mymap, marker);
});
}(infowindow);
The problem is that your infowindow variable is getting recreated in the loop each time, and when the click handler fires, it is looking in a local closure for the most recent version. The updated code creates a new closure that will contain the version of infowindow that is unique to that iteration of the loop.
Related
I have add the following code to a Google Map on a site of mine. The map contains many points pulling from coordinates set in the WordPress backend.
I also want to include some static points which will always stay on the map and am hardcoding their coordinates.
The following is the code I am using and what happens is that the code displays the first marker but not the infobox. Because of this, the code stops and does not continue through the for loop. The issue is at the return function() bit, but I am not sure how to get it working.
var infowindow = new google.maps.InfoWindow({maxWidth: 185});
var setMarker;
var setMarkers = new Array();
var setLocations = [
['<h4>Location1</h4>', 53.4264,-6.2499, '/wp-content/themes/path/to/airport_icon.png'],
['<h4>Location2</h4>', 53.3461,-6.2969, '/wp-content/themes/path/to/train_icon.png'],
['<h4>Location3</h4>', 53.3532,-6.2468, '/wp-content/themes/path/to/train_icon.png'],
['<h4>Location4</h4>', 53.4264,-6.2499, '/wp-content/themes/path/to/dvc_icon.png'],
['<h4>Location5</h4>', 53.4264,-6.2499, '/wp-content/themes/path/to/dvc_icon.png'],
];
for (var i = 0; i < setLocations.length; i++) {
marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(setLocations[i][1], setLocations[i][2]),
icon : setLocations[i][3],
});
setMarkers.push(setMarker);
google.maps.event.addListener(setMarker, 'click', (function(setMarker, i) {
return function() {
infowindow.setContent(setLocations[i][0]);
infowindow.open(map, setMarker);
}
})(setMarker, i));
}
Define your setMarker variable inside the for loop and push it to your markers array:
for (var i = 0; i < setLocations.length; i++) {
var setMarker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(setLocations[i][1], setLocations[i][2])
});
google.maps.event.addListener(setMarker, 'click', (function (setMarker, i) {
return function () {
infowindow.setContent(setLocations[i][0]);
infowindow.open(map, setMarker);
}
})(setMarker, i));
setMarkers.push(setMarker);
}
JSFiddle demo
I have a weird issue with event listeners on google map markers. Basically I want to declare a bunch of markers in a loop, and have each marker have an associated infowindow. The relevant code is:
var markers=[];
var contents = [];
var infowindows = [];
for (i = 0; i < 10; i++) {
markers[i] = new google.maps.Marker({
position: new google.maps.LatLng(40+Math.random()*5, 4+Math.random()*5),
map: map,
title: 'samplemarker'
});
contents[i] = '<div class="popup_container">' +
'</div>';
infowindows[i] = new google.maps.InfoWindow({
content: contents[i],
maxWidth: 300
});
google.maps.event.addListener(markers[i], 'click', function() {
infowindows[i].open(map,markers[i]);
map.panTo(markers[i].getPosition());
});
}
The markers are created correctly, and the infowindows too, since if I do manually infowindows[i].open(map,markers[i]); they are opened correctly. However the listener does not work.
Even weirder: I have another marker, "marker_1" declared outside of the for loop, exactly the same way. If I write:
google.maps.event.addListener(marker_1, 'click', function() {
infowindows[0].open(map,markers[0]);
map.panTo(markers[0].getPosition());
});
The infowindow 0 is opened and the map is panned to marker 0 when marker_1 is clicked. However when writing, at the exact same position, the same lines except for marker_1 replaced with markers[0], a click on the marker 0 has no effect at all.
Thanks for any help and sorry if it's something stupid!
Inside your onclick handler, you don't already have that i value, in your case it would always take last value of i after the end of the loop, i.e 10, and markers[10] doesn't exist as you only have 10 markers.
To make it work you can e.g. add additional property to your markers in array, that would store marker index and use it inside your onlick handler
var markers=[];
var contents = [];
var infowindows = [];
for (i = 0; i < 10; i++) {
markers[i] = new google.maps.Marker({
position: new google.maps.LatLng(40+Math.random()*5, 4+Math.random()*5),
map: map,
title: 'samplemarker'
});
markers[i].index = i; //add index property
contents[i] = '<div class="popup_container"></div>';
infowindows[i] = new google.maps.InfoWindow({
content: contents[i],
maxWidth: 300
});
google.maps.event.addListener(markers[i], 'click', function() {
console.log(this.index); // this will give correct index
console.log(i); //this will always give 10 for you
infowindows[this.index].open(map,markers[this.index]);
map.panTo(markers[this.index].getPosition());
});
}
see corrected example
So I'm trying to figure out a way to change the HTML of Google Maps V3 markers after they have been pulled from the database but before they are pushed up to the array.
When getFishing() is called, I'd like to run convertRate(rate) so that if the rate variable is equal to two or more, it shows a picture which is within the HTML of the Markers themselves. I've tried putting it in the bindInfoWindow4() and I've tried several places within the getFishing() function with no success. Has anyone done this before? Is it possible after the markers have been pushed up to the fishArray?
function getFishing() {
fishingUrl("XML_Fishing.php", function (data) {
var xml = data.responseXML;
var markers = xml.documentElement.getElementsByTagName("marker");
for (var i = 0; i < markers.length; i++) {
var id = markers[i].getAttribute("id");
var title = markers[i].getAttribute("title");
var rate = markers[i].getAttribute("rate");
var Fishhtml = "<img id='1star' src='images/1star.png' style='visibility:hidden'>";
var icon = FishingIcon;
var Fishmark = new google.maps.Marker({
map: map,
position: point,
icon: icon.icon
});
fishArray.push(Fishmark);
bindInfoWindow4(Fishmark, map, Fishinfo, Fishhtml);
}
});
}
function convertRate(rate) {
if (rate >= 2) {
document.getElementById("1star").style.visibility = 'visible';
}
}
function bindInfoWindow4(marker, map, infoWindow, html) {
google.maps.event.addListener(marker, 'click', function () {
infoWindow.setContent(html);
infoWindow.open(map, marker);
});
}
If you change your click listener to display the HTML saved in a member variable of the marker, you can change it at any time. If the InfoWindow is open, you may want to close it and reopen it (or update its content in place, but that gets more complicated).
Something like:
function bindInfoWindow4(marker, map, infoWindow, html) {
marker.myHtmlContent = html;
google.maps.event.addListener(marker, 'click', function() {
infoWindow.setContent(marker.myHtmlContent);
infoWindow.open(map, marker);
});
}
Then update the content by changing the value in marker.myHtmlContent. To make it visible, somthing like this:
marker.myHtmlContent = "<img id='1star' src='images/1star.png' style='visibility:visible'>";
I try to add infowindows for all my markers. But I add only one InfoWindow for last marker.
Can you help me?
for (i = 0; i < events.length; i++) {
var contentString = '<div id="content">'+i+'bla-bla-bla</div>';
var infowindow = new google.maps.InfoWindow({
content: contentString
});
var newMarker = new google.maps.Marker({
position: new google.maps.LatLng(events[i][1], events[i][2]),
map: map,
title: events[i][0]
});
newMarker.categorysassasas = 2;
newMarker.category = events[i][3];
markers.push(newMarker);
google.maps.event.addListener(newMarker, 'click', function() {
infowindow.open(map,newMarker);
});
}
found a similar solution to your question in this stackoverflow: Trying to bind multiple InfoWindows to multiple Markers on a Google Map and failing
Essentially all you'll have to do is wrap your addlistener function within another function to create a closure for your marker variable - so that it is bound to the infowindow. In your case, I believe you'll have to add the listenMarker function outside of your init() and pass in the variable for marker and infowindow, like so..
function listenMarker (marker, info)
{
// so marker is associated with the closure created for the listenMarker function call
google.maps.event.addListener(marker, 'click', function() {
info.open(map, marker);
});
}
and then in your for loop where you call the addlistener event, replace that with:
listenMarker(newMarker, infowindow);
That should do the trick.
I need to have only one InfoWindow open on my Google Map. I need to close all other InfoWindows before I open a new one.
Can someone show me how to do this?
You need to create just one InfoWindow object, keep a reference to it, and reuse if for all the markers. Quoting from the Google Maps API Docs:
If you only want one info window to display at a time (as is the behavior on Google Maps), you need only create one info window, which you can reassign to different locations or markers upon map events (such as user clicks).
Therefore, you may simply want to create the InfoWindow object just after you initialize your map, and then handle the click event handlers of your markers as follows. Let's say you have a marker called someMarker:
google.maps.event.addListener(someMarker, 'click', function() {
infowindow.setContent('Hello World');
infowindow.open(map, this);
});
Then the InfoWindow should automatically close when you click on a new marker without having to call the close() method.
Create your infowindow out of the scope so that you can share it.
Here is a simple example:
var markers = [AnArrayOfMarkers];
var infowindow = new google.maps.InfoWindow();
for (var i = 0, marker; marker = markers[i]; i++) {
google.maps.event.addListener(marker, 'click', function(e) {
infowindow.setContent('Marker position: ' + this.getPosition());
infowindow.open(map, this);
});
}
I had the same problem but the best answer didn't solve it completely, what I had to do in my for statement was using the this relating to my current marker. Maybe this helps someone.
for(var i = 0; i < markers.length; i++){
name = markers[i].getAttribute("name");
address = markers[i].getAttribute("address");
point = new google.maps.LatLng(parseFloat(markers[i].getAttribute("lat")), parseFloat(markers[i].getAttribute("lng")));
contentString = '<div style="font-family: Lucida Grande, Arial, sans-serif;>'+'<div><b>'+ name +'</b></div>'+'<div>'+ address +'</div>';
marker = new google.maps.Marker({
map: map,
position: point,
title: name+" "+address,
buborek: contentString
});
google.maps.event.addListener(marker, 'click', function(){
infowindow.setContent(this.buborek);
infowindow.open(map,this);
});
marker.setMap(map);
}
a tad late, but I managed to have only one infowindow open by maken infowindow a global variable.
var infowindow = new google.maps.InfoWindow({});
then inside the listner
infowindow.close();
infowindow = new google.maps.InfoWindow({
content: '<h1>'+arrondissement+'</h1>'+ gemeentesFiltered
});
infowindow.open(map, this);
Declare a globar var selectedInfoWindow; and use it to hold the opened info window:
var infoWindow = new google.maps.InfoWindow({
content: content
});
// Open the infowindow on marker click
google.maps.event.addListener(marker, "click", function() {
//Check if there some info window selected and if is opened then close it
if (selectedInfoWindow != null && selectedInfoWindow.getMap() != null) {
selectedInfoWindow.close();
//If the clicked window is the selected window, deselect it and return
if (selectedInfoWindow == infoWindow) {
selectedInfoWindow = null;
return;
}
}
//If arrive here, that mean you should open the new info window
//because is different from the selected
selectedInfoWindow = infoWindow;
selectedInfoWindow.open(map, marker);
});
You need to keep track of your previous InfoWindow object and call the close method on it when you handle the click event on a new marker.
N.B It is not necessary to call close on the shared info window object, calling open with a different marker will automatically close the original. See Daniel's answer for details.
Basically you want one function that keeps reference to one new InfoBox() => delegate the onclick event.
While creating your markers (in a loop) use bindInfoBox(xhr, map, marker);
// #param(project): xhr : data for infoBox template
// #param(map): object : google.maps.map
// #param(marker): object : google.maps.marker
bindInfoBox: (function () {
var options = $.extend({}, cfg.infoBoxOptions, { pixelOffset: new google.maps.Size(-450, -30) }),
infoBox = new window.InfoBox(options);
return function (project, map, marker) {
var tpl = renderTemplate(project, cfg.infoBoxTpl); // similar to Mustache, Handlebars
google.maps.event.addListener(marker, 'click', function () {
infoBox.setContent(tpl);
infoBox.open(map, marker);
});
};
}())
var infoBox is assigned asynchronously and kept in memory. Every time you call bindInfoBox() the return function will be called instead. Also handy to pass the infoBoxOptions only once!
In my example I've had to add an extra param to the map as my initialization is delayed by tab events.
InfoBoxOptions
Here is a solution that doesn't need to create only one infoWindow to reuse it. You can continue creating many infoWindows, the only thing you need is to build a closeAllInfoWindows function, and call it before open a new infowindow.
So, keeping your code, you just need to:
Create a global array to store all the infoWindows
var infoWindows = [];
Store each new infoWindow in the array, just after the infoWindow = new...
infoWindows.push(infoWindow);
Create the closeAllInfoWindows function
function closeAllInfoWindows() {
for (var i=0;i<infoWindows.length;i++) {
infoWindows[i].close();
}
}
In your code, call to closeAllInfoWindows() just before open the infoWindow.
Regards,
One smart easy way to do this with jQuery is the following :
google.maps.event.addListener(marker, 'click', function (e) {
jQuery(".gm-ui-hover-effect").click();
marker.info.open(map, this);
});
It will click on all the closing buttons amongst your tooltips.
My approach allows you to toggle the infoWindow as well.
Global space
var infoWindow = new google.maps.InfoWindow();
infoWindow.setContent(contentString);
var lastInfoWindow;
Local space
marker.addListener("click", (e) => {
if (lastInfoWindow === e.domEvent.srcElement) {
infoWindow.close();
lastInfoWindow = null;
} else {
infoWindow.open({
anchor: marker,
map,
shouldFocus: false,
});
lastInfoWindow = e.domEvent.srcElement;
}
});
Solved it this way:
function window(content){
google.maps.event.addListener(marker,'click', (function(){
infowindow.close();
infowindow = new google.maps.InfoWindow({
content: content
});
infowindow.open(map, this);
}))
}
window(contentHtml);
Google Maps allows you to only have one info window open. So if you open a new window, then the other one closes automatically.