I have a map that uses infoBubble.js.
In this map there is an array of locations that I iterate through.
The infoBubble should pop up when the custom icon is clicked but for some reason it only ever opens up the first data item.
Does anyone have an idea as to why that may happen?
I have developed the code for it here;
var arrMarkers = [
['Santiago de Cuba', 20.040450354169483, -75.8331298828125],
['Las Tunas', 20.97682772467435, -76.9482421875],
['Camaguey', 21.39681937408218, -77.9205322265625],
['Playa Santa Lucia', 21.555284406923192, -77.0526123046875],
['Santa Clara', 22.421184710331854, -79.9639892578125],
['Cienfuegos', 22.161970614367977, -80.4473876953125],
['Havana', 23.12520549860231, -82.3919677734375],
['San Cristobel', 22.730590425493833, -83.045654296875],
['Pinar del Rio', 22.43641760076311, -83.69384765625]
];
var arrInfoWindowsCuba = [];
var arrInfoWindows = [];
arrMarkers[i] = marker;
function init() {
var mapCenter = new google.maps.LatLng(21.616579336740603, -78.892822265625);
map = new google.maps.Map(document.getElementById('map_canvas'), {
zoom: 7,
center: mapCenter,
mapTypeId: google.maps.MapTypeId.TERRAIN
});
var image = '/wp-content/themes/Shootcuba/images/map-icon.png';
for (i = 0; i < arrMarkers.length; i++) {
var marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(arrMarkers[i][1], arrMarkers[i][2]),
icon: image
});
var infoBubble = new InfoBubble({
content: '<div class="phoneytext">' + arrMarkers[i][0] + '<div class="left-col2"></div></div>',
boxClass: 'info-box',
alignBottom: true,
pixelOffset: new google.maps.Size(-150, -40),
maxWidth: 300,
padding: 0,
closeBoxMargin: '0px',
borderColor: '#ffffff',
borderRadius: '0',
maxWidth: 535,
disableAutoPan: false,
hideCloseButton: false,
backgroundClassName: 'phoney'
});
google.maps.event.addListener(marker, 'click', function () {
infoBubble.open(map, marker, i);
console.log(arrMarkers);
});
arrMarkers[i] = marker;
arrInfoWindowsCuba[i] = infoBubble;
}
}
Here's a working example. I took out a few of the arrays you had (I wasn't entirely sure what they were all for, and they were causing errors in just the snippet you posted), but otherwise is pretty true to what you were doing. The big difference is that I made a separate function for creating the markers. This was mainly done to keep the scope of the click events separate from one another, since the click event always triggering the last event indicates to me that the scopes aren't properly separate.
In particular, what I believe was happening is that the event function you kept overriding values to marker and infoBubble, and the event listener would refer to the current values of those variables, not the values when you first attach the listener. Making a separate function call to maintain the scope for the events strikes me as the cleanest solution.
Related
UPDATE
I have added the error up here as its makes it easier to read. Below is the error I now get with that update. P.S it should be InfoBox and not Infobox, I think I made that error somewhere when I typed it up :). So where am I going wrong now? I am not even sure what this error message is saying :
Uncaught TypeError: Cannot read property 'style' of null
InfoBox.setBoxStyle_
InfoBox.setOptions
(anonymous function) PAGE.php:101
N.trigger main.js:23
xK.(anonymous function).e
(anonymous function)
N.trigger main.js:23
F.ie
F.sm
(anonymous function) main.js:11
N.trigger main.js:23
F.Jk
(anonymous function)
OLD POST
I have a custom Google Maps V3 with custom markers being generated with a XML file that is built with a custom DB. I have posted my code below, this all works, only thing is I have now built in the Infobox plugin, which gives me more control over the styling of the marker styles. However they, unlike the Google InfoWindow does not close automatically when another marker is clicked.
This is my load function, which sets the map and build the marker with my XML file,
function load() {
var map = new google.maps.Map(document.getElementById("map"), {
center: new google.maps.LatLng(54.500, 355.000),
zoom: 5,
mapTypeId: 'roadmap'
});
downloadUrl("genxml.php?id=<?php echo $VarToUse ?>",function(data) {
var xml = data.responseXML;
var markers = xml.documentElement.getElementsByTagName("marker");
for (var i = 0; i < markers.length; i++) {
var name = markers[i].getAttribute("name");
var number = markers[i].getAttribute("number");
var address = markers[i].getAttribute("address");
var type = markers[i].getAttribute("type");
var point = new google.maps.LatLng(parseFloat(markers[i].getAttribute("lat")),parseFloat(markers[i].getAttribute("lng")));
var myOptions = {
content: "<b class=\"infoBox_links\">" + name + "</b> <br/>" + address
,disableAutoPan: false
,maxWidth: 0
,pixelOffset: new google.maps.Size(-90, -125)
,zIndex: null
,boxStyle: {
background: "#FFFFFF",
border: "1px solid grey",
width: "170px",
height: "70px",
padding: "8px",
fontSize: "12px",
overflow: "hidden"
}
,closeBoxMargin: "-5px"
,closeBoxURL: "imgs/x_google.png"
,isHidden: false
,pane: "floatPane"
,enableEventPropagation: false
};
var icon = customIcons[type] || {};
var marker = new google.maps.Marker({
map: map,
position: point,
icon: icon.icon
});
bindInfoWindow(marker, map, myOptions); //function call to set the markers!
}
});
} //End of function load()
As you can see I have a function that is called within the load, this is my current code, which works fine
function bindInfoWindow(marker, map, myOptions) {
google.maps.event.addListener(marker, 'click', function() {
var ib = new InfoBox(myOptions);
ib.open(map, marker);
});
} //End of bindInfoWindow function!!
This works, custom info boxes with the styles I need are generated, however the current 'open' info box does not close when a new marker is click. And from a number of different attempts and ideas from other people, I am currently working with :
function bindInfoWindow(marker, map, myOptions) {
var ibs = [];
var closeInfoBox = function() {
for (var i in ibs) {
ibs[i].close();
}
}
var ibIndex = ibs.push(new Infobox()) - 1,
ib = ibs[ibIndex];
google.maps.event.addListener(marker, 'click', function() {
closeInfoBox();
ib.setOptions(myOptions);
//var ib = new InfoBox(myOptions);
ib.open(map, marker);
});
}
This code come from Opening only a infobox at a time when multiple markers are displayed on the map
However this only gives me errors, either I have not copied this code right or my set up for the maps different enough for this code not to work (with I don't think is the case). I think it is something I am not doing right. Right now as the code stands, its this line that does not want to work,
var ibIndex = ibs.push(new Infobox()) - 1,
Comes back in the console log with
Uncaught ReferenceError: Infobox is not defined PAGE.php:101
bindInfoWindow PAGE.php:101
(anonymous function) PAGE.php:84
request.onreadystatechange
All ideas most welcome,
Many thanks
Glenn.
Try this. You need the array and closeInfoBox specified outside of the load function, otherwise you just keep resetting the array every time.
var ibs = [];
function closeInfoBox() {
for (var i in ibs) {
ibs[i].close();
}
}
And then you need to push the infobox into the array within the bind function:
function bindInfoWindow(marker, map, myOptions) {
var ib = new Infobox(myOptions);
ibs.push(ib);
google.maps.event.addListener(marker, 'click', function() {
closeInfoBox();
ib.open(map, marker);
});
}
I haven't tested this, but it's similar to what I've done in my code at various points.
However they, unlike the Google InfoWindow does not close automatically when another marker is clicked
This is new to me, a google.maps.InfoWindow will never be closed automatically .
When you only want to have a single InfoBox open use only a single InfoBox-instance for all markers:
function bindInfoWindow(marker, map, myOptions) {
google.maps.event.addListener(marker, 'click', function() {
//create a Infobox that will be used by each marker
if(!this.getMap().get('ib')){this.getMap().set('ib',new InfoBox());}
var ib = this.getMap().get('ib');
ib.setOptions(myOptions);
ib.open(this.getMap(),this);
});
}
I am a begginner in javascript, and I know that my code is a little messy. I feel as though I may have stepped on my own foot here but I am really hoping that there is some sort of work around.
What I am wondering is what the best way to add the event listener to the anchors is. Right now, I have a loop that sets all the markers on the map (so I didn't have to write the line of code each time for each marker) but looking back at it I am wondering if there is even a way to add an event listener now.
I have a list of links on the page, and I have an array full of data that i use to tag various things. What I need is to be able to click on the link (where it says "map it!") and for the info window to be prompted, and then I need to toggle that so that it closes if another one is opened
the website can be found here:
http://www.michiganwinetrail.com
And here is the full javascript page
http://www.michiganwinetrail.com/mainmap2.js
the code for the loop that I need to edit (which can be found at the bottom of that javascript link) is as follows:
function loadMap() {
var centerMich = new google.maps.LatLng(44.229457, -85.100098);
var myOptions = {
center: centerMich,
zoom: 7,
scrollwheel: false,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("mainMichMap"), myOptions);
var homesouthwestDiv = document.createElement('div');
var homenorthwestDiv = document.createElement('div');
var homesoutheastDiv = document.createElement('div');
homesouthwestDiv.index = 1;
homenorthwestDiv.index = 2;
homesoutheastDiv.index = 3;
var homeControl = {
southwest: swRegions(homesouthwestDiv, map),
northwest: nwRegions(homenorthwestDiv, map),
southeast: seRegions(homesoutheastDiv, map),
};
map.controls[google.maps.ControlPosition.RIGHT_TOP].push(homesouthwestDiv);
map.controls[google.maps.ControlPosition.RIGHT_TOP].push(homenorthwestDiv);
map.controls[google.maps.ControlPosition.RIGHT_TOP].push(homesoutheastDiv);
for (var i=0; i<=locations.length; i++) {
locations[i][0] = new google.maps.Marker({
position: new google.maps.LatLng(locations[i][1], locations[i][2]),
title: locations[i][3],
map: map,
content: locations[i][4]});
var infowindow = new google.maps.InfoWindow({
});
google.maps.event.addListener(locations[i][0], 'click', function() {
infowindow.setContent(this.content);
infowindow.open(map,this);
});
}
}
On click of the link you need to do something like this:
var myHtml = "<h2 class=\"firstHeading\">" + winery.name + "</h2>";
map.openInfoWindowHtml(new GLatLng(winery.fltLat, winery.fltLng), myHtml);
I had done a similar demo some time ago: http://dipoletech.com/beermenu/
I am having trouble with my Google Maps web app, i am trying to add markers to a map and then when the user clicks on that marker, the name of marker appears.
I store information about each marker in an array like so:
var citymap = {};
citymap['edinburgh'] = {
center: new google.maps.LatLng(55.934120, -3.226569),
population: 284,
image:clueImage,
color: '#ff0000',
elementId:'clue1'
};
citymap['clue2'] = {
center: new google.maps.LatLng(55.970783, -3.164594),
population: 284,
image:clueImage,
color: '#ff0000',
elementId:'clue2'
}
citymap['clue4'] = {
center: new google.maps.LatLng(55.939583, -3.202092),
population: 284,
image:clueImage,
color: '#ff0000',
elementId:'clue3'
}
citymap['clue9'] = {
center: new google.maps.LatLng(0, 0),
population: 284,
image:clueImage,
color: '#ff0000',
elementId:'clue1'
}
I then loop through the array and add the marker to the map:
for (var city in citymap) {
var playerMarker = new google.maps.Marker({
position: citymap[city].center,
map: map,
icon: citymap[city].image
});
google.maps.event.addListener(playerMarker, "click", function() {
var elementConnected = citymap[city].elementId;
console.log(elementConnected);
});
}
Everything works as intended, however when I click on any of the markers, they all log the value of the last marker in the array. I know this is because thats where the loop finished, but I can not think of a way of making this work.
Help would be very helpful!
I think this is the universal "closure" problem in Javascript that people have. Someone might give a better way of doing this, but I think you need something like this:
google.maps.event.addListener(playerMarker, "click", (function() {
return function () {
var elementConnected = citymap[city].elementId;
console.log(elementConnected);
};
})(city));
Or if possible, depending on what else you're doing inside the click event, maybe this:
google.maps.event.addListener(playerMarker, "click", (function(elementId) {
return function () {
var elementConnected = elementId;
console.log(elementConnected);
};
})(citymap[city].elementId));
My website has a number of pages that show a Google map with a bunch of markers, here's an example.
As you can see, the maps take a long time to load and I'm looking for ways to improve this. I was hoping to use GeoWebCache to cache the map tiles on the server, but I was informed that this would violate the terms of use for Google maps.
The code that I use to display a map and add a marker is appended below. It's a pretty straightforward usage of the Google Maps V3 JavaScript API, so I don't think there's much scope for optimizing it. Are there any obvious steps I could take to reduce the map-loading time?
SF.Map = function(elementId, zoomLevel, center, baseImageDir) {
this._baseImageDir = baseImageDir;
var focalPoint = new google.maps.LatLng(center.latitude, center.longitude);
var mapOptions = {
streetViewControl: false,
zoom: zoomLevel,
center: focalPoint,
mapTypeId: google.maps.MapTypeId.ROADMAP,
mapTypeControl: false
};
this._map = new google.maps.Map(document.getElementById(elementId), mapOptions);
this._shadow = this._getMarkerImage('shadow.png');
};
SF.Map.prototype._getMarkerImage = function(imageFile) {
return new google.maps.MarkerImage(this._baseImageDir + '/map/' + imageFile);
};
SF.Map.prototype._addField = function(label, value) {
return "<span class='mapField'><span class='mapLabel'>" + label + ": </span><span class='mapValue'>" + value + "</span></span>";
};
/**
* Add a marker to the map
* #param festivalData Defines where the marker should be placed, the icon that should be used, etc.
* #param openOnClick
*/
SF.Map.prototype.addMarker = function(festivalData, openOnClick) {
var map = this._map;
var markerFile = festivalData.markerImage;
var marker = new google.maps.Marker({
position: new google.maps.LatLng(festivalData.latitude, festivalData.longitude),
map: map,
title: festivalData.name,
shadow: this._shadow,
animation: google.maps.Animation.DROP,
icon: this._getMarkerImage(markerFile)
});
var bubbleContent = "<a class='festivalName' href='" + festivalData.url + "'>" + festivalData.name + "</a><br/>";
var startDate = festivalData.start;
var endDate = festivalData.end;
if (startDate == endDate) {
bubbleContent += this._addField("Date", startDate);
} else {
bubbleContent += this._addField("Start Date", startDate) + "<br/>";
bubbleContent += this._addField("End Date", endDate);
}
// InfoBubble example page http://google-maps-utility-library-v3.googlecode.com/svn/trunk/infobubble/examples/example.html
var infoBubble = new InfoBubble({
map: map,
content: bubbleContent,
shadowStyle: 1,
padding: 10,
borderRadius: 8,
borderWidth: 1,
borderColor: '#2c2c2c',
disableAutoPan: true,
hideCloseButton: false,
arrowSize: 0,
arrowPosition: 50,
arrowStyle: 0
});
var mapEvents = google.maps.event;
// either open on click or open/close on mouse over/out
if (openOnClick) {
var showPopup = function() {
if (!infoBubble.isOpen()) {
infoBubble.open(map, marker);
}
};
mapEvents.addListener(marker, 'click', showPopup);
} else {
mapEvents.addListener(marker, 'mouseover', function() {
infoBubble.open(map, marker);
});
mapEvents.addListener(marker, 'mouseout', function() {
infoBubble.close();
});
}
};
You could try "lazy loading" the google map, something like this:
var script=document.createElement("script");
script.type="text/javascript";
script.async=true;
script.src="http://maps.google.com/maps/api/js?sensor=false&callback=handleApiReady";
document.body.appendChild(script);
Or even like this is how I go it for Facebook AIP so that this doesn't slow down the initial load time.
$.getScript('http://connect.facebook.net/en_US/all.js#xfbml=1', function() {
FB.init({appId: opt.facebook_id, status: true, cookie: true, xfbml: true});
});
Example: http://blog.rotacoo.com/lazy-loading-instances-of-the-google-maps-api
Also looking at your HTML you have a lot of JS which should be in an external JS file and not inline, can you not pass a array instead of having a lot of duplicate inline code.
One option is to take a static snapshot and create the map behind it. once map is fully loaded, replace the dynamic one with the static map.
I also suggest that you take a look at:
https://developers.google.com/maps/documentation/staticmaps/
it might be that you can provide a simpler solution to your site without using the full dynamic maps api.
I have a map with streetview panorama, an infowindow and a draggable marker. After the marker is dragged, a function requests the getPanoramaByLocation to see whether the view service is available. If it's not, it closes the infowindow. That is great but when I click again on the marker the infowindow does not open anymore. Do you know what is wrong please?
tx
var sv = new google.maps.StreetViewService();
function initialize() {
var optionMap = {
zoom: 13,
center: new google.maps.LatLng(47.390251,0.68882),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var myMap = new google.maps.Map(document.getElementById("mapDiv"), optionMap);
var optionsMarker = {
position: new google.maps.LatLng(47.390251,0.68882),
map: myMap,
draggable: true,
title: "my marker"
}
var marker = new google.maps.Marker(optionsMarker);
var infowindowDiv = '<div id="streetview" style="width:300px;height:200px;"' +'</div>';
var infoWindow = new google.maps.InfoWindow({
content: infowindowDiv,
position: myMap.getCenter() });
google.maps.event.addListener(marker, 'click', function() {
infoWindow.open(myMap, marker);
});
google.maps.event.addListener(infoWindow, 'domready', function() {
var panorama = new
google.maps.StreetViewPanorama(document.getElementById("streetview"));
panorama.setPosition(infoWindow.getPosition());
google.maps.event.addListener(marker, 'dragend', function(event) {
sv.getPanoramaByLocation(event.latLng, 50, processSVData);});
function processSVData(data, status) {
if (status == google.maps.StreetViewStatus.OK) {
var markerPanoID = data.location.pano;
// Set the Pano to use the passed panoID
panorama.setPano(markerPanoID);
panorama.setPov({
heading: 270,
pitch: 0,
zoom: 1
});
panorama.setVisible(true);
}
else {
infoWindow.close();
infoWindow = null;
};
}
});
}
From the GMaps API docs:
close() None Closes this InfoWindow by removing it from the DOM structure.
https://developers.google.com/maps/documentation/javascript/reference#InfoWindow
So... If you close the infowindow, it's gone. For ever. All the more so if you REALLY make sure it's gone by saying "infoWindow = null;" You have to make a new one. My advice would be to refactor your code to have a separate function that creates the infowindow on demand and returns it. In your click event definition, check whether infowindow is null, and if so, grab a new one.
HTH
As the other answer explain, the infobox html is removed from DOM when the user clicks the close button, but the JS object still there.
When you create an infowindow you must call the open method to see it on the map
myInfoWindow.open(mymap);
So, if you listen to the 'closeclick' event and keep the infowindow state
var infoWindowClosed = false;
google.maps.event.addListener(myInfoBox, "closeclick", function () {
infoWindowClosed = true;
});
you can reopen myInfoWindow on demand
if(infoWindowClosed){
myInfoWindow.open(mymap);
}