InfoWindow - Google map API - javascript

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.

Related

Google maps API add infowindow to array of markers on click event

I'm trying to make an array of markers and have each marker open an info window with related info onclick but I either get a cannot read 'apply' property of undefined or it opens on the wrong marker; My 2 attempts were:
Cannot read apply:
var infowindow = new google.maps.Infowindow();
for (var i=0;i<popular.length;i++){
var mkrLatLng = new google.maps.LatLng(popular[i][1], popular[i][2]);
var marker = new google.maps.Marker({
map: map,
title: popular[i][0],
position: mkrLatLng
});
markers.push(marker);
}
for (var i=0;i<markers.length;i++){
markers[i].addListener('click', (function(){
console.log('click');
cont = "<div> " + markers[i].title + " </div>";
infowindow.setContent(cont);
infowindow.open(map, marker);
})(markers));
}
Opens on wrong marker (Anonymous function is wrapped in another function since it got rid of the undefined error, I don't understand why):
var infowindow = new google.maps.Infowindow();
for (var i=0;i<popular.length;i++){
var mkrLatLng = new google.maps.LatLng(popular[i][1], popular[i][2]);
var marker = new google.maps.Marker({
map: map,
title: popular[i][0],
position: mkrLatLng
});
marker.addListener('click', function(){(function(){
console.log('click');
cont = "<div> " + marker.title + " </div>";
infowindow.setContent(cont);
infowindow.open(map, marker);
})(marker)});
markers.push(marker);
}
Your structure is wrong
this is example enter link description here
//wrong
for(var i=0;i<3;i++){
var count=i
$('<p>wrong</p>').on('click',function(){
alert(count)
}).appendTo('body')
}
//what you want
for(var i=0;i<3;i++){
createEl(i)
}
function createEl(i){
$('<p>correct</p>').on('click',function(){
alert(i)
}).appendTo('body')
}
Hope this will help you understand why you're wrong
So you need to change your code to this
for(){
createMarker(parameter)
}
function createMarker(parameter){
var marker=...,
infowindow=....
marker.addlistener(...)
.......
}
Try your second code, make var infowindow global, maybe it'll work, I'm not sure.
Or else you can new every infowindow in forloop, and push into array, just like you handle markers.
var mark = new google.maps.Marker({
map: map,
position: place.geometry.location,
optimized: false,
icon: 'red-dot.png'
}),
infowindow = new google.maps.InfoWindow()
infowindow.setContent(place.name)
infowindows.push(infowindow)
//then addlistener to marker
//markers.push(marker)
To open just one infowindow, use infowindow.close() to close all infowindows,
then call infowindow.open(map,marker)

Change icon on mouseover/out fails

I'm tearing my hair out over this code. As you may guess, I'm relatively new to coding. I would appreciate any insight you have into why it is not working.
I am trying to change marker icons on mouseover/out. I am trying to create and add listeners in a for-loop. In the loop, the markers are created from an array of locations and pushed to another array, a listener is added for mouseover events to change the icon, and another listener is added for mouseout events to nullify the marker icon. The code below displays the map, adds the markers and seems to listen for mouseover and mouseout events (the cursor changes when hovering over a marker), but the icon does not change at these events.
function initialize(){
//Marker locations
var NYC = new google.maps.LatLng(40.721505, -74.004783);
var LA = new google.maps.LatLng(34.049519, -118.238698);
var Chicago = new google.maps.LatLng(41.877461, -87.624352);
var Seattle = new google.maps.LatLng(47.606747, -122.330349);
var Miami = new google.maps.LatLng(25.788661, -80.226617);
var Boston = new google.maps.LatLng(42.357913, -71.059217);
var Houston = new google.maps.LatLng(29.758182, -95.364213);
var KansasCity = new google.maps.LatLng(39.097781,-94.588079);
var locations = new Array(NYC, LA, Chicago, Seattle, Miami, Boston, Houston, KansasCity);
//Array to store markers
var markers = new Array ();
//Icon
var gif = 'http://demers-ambulances.com/assets/img/news/mapPinOver2.gif';
//Map options
var mapOptions = {
center: KansasCity,
zoom: 5,
zoomControl: true,
zoomControlOptions: {
style: google.maps.ZoomControlStyle.SMALL,
position: google.maps.ControlPosition.TOP_LEFT
},
mapTypeControl: true,
mapTypeControlOptions: {
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,
position: google.maps.ControlPosition.TOP_RIGHT,
},
};
//Create map
var map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
//Create markers and add listener to change marker icon on mouseover/out
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);
});
};
};
google.maps.event.addDomListener(window, 'load', initialize);
Thanks for your help :)
Closure problem: there is error reported:
Uncaught TypeError: Cannot read property 'setIcon' of undefined
Code has to be changed to:
(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);
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
After further experimenting with the code that Anto and Farid recommended I found another solution. For this solution a function is created outside the initialize function to add listeners to the markers, and then called when each marker is created in the for loop. If you have any thoughts on this, please comment below. I have no clue if this is a good way to do this, I just know it works :)
for (i = 0; i<locations.length; i++){
var marker = new google.maps.Marker({
position: locations[i],
draggable: true,
map: map,
});
animateit(marker);
markers.push(marker);
};
};
function animateit(marker) {
google.maps.event.addListener(marker, 'mouseover', function() {
marker.setIcon(gif);
});
google.maps.event.addListener(marker, 'mouseout', function() {
marker.setIcon(null);
});

Map not adding click event handlers to markers

The code below will add markers to my map. But the add Listener event never gets added to each marker.
var mapDiv = document.getElementById("google-map");
var infowindow = new google.maps.InfoWindow({
content: 'test'
});
var map = new google.maps.Map(mapDiv);
map.setCenter(new GLatLng(53.635784, 6.943359));
map.setZoom(5);
for (var i = 0; i < data.length; i++) {
var dataMarker = data[i];
var marker = new GLatLng(dataMarker.Latitude, dataMarker.Longitude);
map.addOverlay(new google.maps.Marker(marker, {
title: dataMarker.Name,
html: dataMarker.HtmlAttributes[0]
}));
google.maps.event.addListener(marker, 'click', function () {
infoWindow.setContent(this.html);
infoWindow.open(map, this);
});
}
What am I doing wrong?
Oh and I am using Maps v2.
You did not add any listener to marker.
var marker = new GLatLng(dataMarker.Latitude, dataMarker.Longitude);
marker is not a google.maps.Marker , it's a google.maps.LatLng , which will not respond to mouse-events, because it's not an UI-element, it's just a javascript-object
You created event listeners which are most probably connected with last marker.
You have to link info window with marker in separate function. Last part of your code should be written as:
addEventListener(marker, infoWindow, map);
}
function addEventListener(marker, infoWindow, map) {
google.maps.event.addListener(marker, 'click', function () {
infoWindow.setContent(marker.html);
infoWindow.open(map, marker);
});
}
See also An array of infoWindow in Google maps api and link with explanation about closures.
It looks like you have a mix of v2 and v3 code in that snippet, and so the whole thing is unlikely to work.
For example, you're using new google.maps.Infowindow (v3) in the same place as GLatLng.
My suggestion would be to change the bootstrap on the page to v3, and delete any references to v2 objects (like GLatLng). Make sure you're loading the API like this:
<script src="//maps.googleapis.com/maps/api/js?…"></script>

Variables overriden into a for loop

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.

Have just one InfoWindow open in Google Maps API v3

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.

Categories