i'm a newbie in javascript
use custom overlay but always detect 'cannot read property 'setContent' of undefined'
my javascript code is https://github.com/SaneMethod/CGWin/blob/master/src/cGWin.js
and i use jquery because of parsing Exel file
////https://github.com/SaneMethod/CGWin/blob/master/src/cGWin.js/////
function GenCustomWindow () {
var CustomWindow = function () {
....
}
}
////parsing code////
$(document).ready(function () {
$.ajax({
type: "GET",
url: "",
datatype: "text",
success: function (data) { processData(data); }
});
});
function processData(allText) {
....
var info = new GenCustomWindow();
for(i = 0;i < name.length;i++)
{
marker = new google.maps.Marker({
position: new google.maps.LatLng(a, b),
map: map,
icon: markerImage,
optimized: false
});
google.maps.event.addListener(marker, 'click', (function (marker, i) {
return function () {
content =
'<div class="iw-title">' +
name[i] + '</div>' +
'<div class="iw-content">' +
'<div class="iw-subTitle">' + add[i] + '</div>' +
'</div>' +
'<div class="iw-bottom-gradient"></div>' +
'</div>';
info.CustomWindow.setContent('content');
}
})(marker, i));
}
}
there is always error in info.CustomWindow.setContent
why is this code an error?
and can you recommend another custom infowindow?
GenCustomWindow() returns a CustomWindow, which means info is alread a CustomWindow.
Change
info.CustomWindow.setContent('content');
to
info.setContent('content');
And everything should work fine.
I'm making website where I bring information about multiple events using XHR (Ajax) call. Every event has information like name, address and city. I'm listing all these events using innerHTML. Every event has button "show on map" which puts marker to that event's address and shows it on the google map. My problem is that every time I press "show on map" button on any event, it puts marker of the last event's address into a map. I can't figure out right way to make it work so it uses right address and not always the last one. I currently have 10 events (since info.length is 10) but all 10 buttons make the marker in the same place. All help is appreciated!
Here's how I do it:
function bringInfo() {
var info = JSON.parse(req.responseText);
for (var i = 0; i < info.length; i++) {
header = info[i].title;
description = info[i].description;
address = info[i].contact_info.address;
city = info[i].contact_info.city;
link = info[i].contact_info.link;
pic = info[i].image.src;
makeEvent(header,description,address,city,link,pic);
}
}
Function where I make event with InnerHTML:
function makeEvent(header, description, address, city, link, pic) {
var newDiv = document.createElement('div');
var containerElem = document.getElementById('container');
var eventHTML = "<div class = 'panel'> <h1 id ='header'>" + header + "</h1>" +
"<img src = '" + pic + "'>" +
"<p>" + description + "</p>" +
"<p>Address:" + address +"</p>" +
"<p>City:" + city +"</p>" +
"<p>Link: <a href ='" + link + "'>" + link + "</a></p>" +
"<button onclick ='geocodeAddress(address)'>Show on map" + "</button>";
newDiv.innerHTML = eventHTML;
newDiv.className += "panel";
containerElem.appendChild(newDiv);
}
EDIT:
var myMap;
function geocodeAddress(address) {
var coder = new google.maps.Geocoder();
coder.geocode({'address': address}, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
myMap.setCenter(results[0].geometry.location);
putMarker (results[0].geometry.location, address);
} else {
alert('Geocoding didnt work. Reason: ' + status);
}
});
}
function putMarker(p, text) {
var ownMarker =
new google.maps.Marker({
position: p,
map: myMap,
title: text
});
}
function initMap() {
myMap = new google.maps.Map(document.getElementById('map'), {
center: {lat: 33.33, lng: 33.33},
zoom: 15
});
}
I think you should change this part:
"<button onclick ='geocodeAddress(address)'>Show on map" + "</button>"
to
"<button onclick ='geocodeAddress(\"' + address + '\")'>Show on map" + "</button>"
Currently your onclick event calls geocodeAddress() function with value of the global variable address. The global variable address contains the value for last element in your for loop.
I have the following 2 functions to pull in, geocode, and place markers in a google map.
I keep getting a TypeError: adds[i] is undefined, which of course is causing the rest of the map to bomb.
Here is my code:
// Place Markers on the Map
var PlaceMarkers = function (iw, adds, gc) {
var image = {url: "http://meatmysite.com/Images/star2.png", size: new google.maps.Size(24, 24)};
var aCt = adds.length;
for(var i = 0; i < aCt; ++i) {
GetLatLng(gc, adds[i].address, function(pos) {
if(pos) {
var ipop = '<h1>' + adds[i].title + '</h1>'; // <----- TypeError: adds[i] is undefined
if(!isBlank(adds[i].url)){
ipop += '' + adds[i].url + '<br />';
}
ipop += '<div class="map_item_content" id="mi_content' + i + '">' + adds[i].content + '</div>';
if(!isBlank(adds[i].mainphone)){
ipop += '<br /><strong>Phone:</strong> ' + adds[i].mainphone + '';
}
if(!isBlank(adds[i].mainemail)){
ipop += '<br /><strong>Email:</strong> ' + adds[i].mainemail + '';
}
console.log('HEY NOW: ' + pos.toString() + ' - Location Found!');
var mark = new google.maps.Marker({title: adds[i].title, position: pos, map: map, icon: image, html: ipop});
google.maps.event.addListener(mark, 'click', function(){
iw.setContent(this.html);
iw.open(map, this);
});
}
});
}
};
// Get Lat/Lng Location
var GetLatLng = function(gc, add, f) {
var ret = '';
gc.geocode({'address': add}, function(res, status) {
if (status == 'OK') {
f(res[0].geometry.location);
console.log('Found Here: ' + ret.toString());
}
});
return -1;
};
DEMO RETURNED DATA FOR adds
[
{
"address": "1 My Street Gilbert, AZ 85234",
"title": "My Title 1",
"url": "http://www.myurl.com/",
"mainphone": null,
"mainemail": null,
"content": "1 My Street<br />Gilbert, AZ 85234"
},
{
"address": "2 My Street North Richland Hills, TX 76182",
"title": "My Title 2",
"url": null,
"mainphone": null,
"mainemail": null,
"content": "2 My Street<br />North Richland Hills, TX 76182"
}
]
One option, pass the complete "address" object into the GetLatLng function, and from there into its callback (so you get function closure on it):
// Get Lat/Lng Location
var GetLatLng = function (gc, add, f) {
gc.geocode({
'address': add.address
}, function (res, status) {
if (status == 'OK') {
f(res[0].geometry.location, add);
}
});
};
Then use it like this inside the callback (you could pass just the index into the array also):
GetLatLng(gc, adds[i], function (pos, add) {
if (pos) {
var ipop = '<h1>' + add.title + '</h1>';
if (!isBlank(add.url)) {
ipop += '' + add.url + '<br />';
}
ipop += '<div class="map_item_content" id="mi_content' + i + '">' + add.content + '</div>';
if (!isBlank(add.mainphone)) {
ipop += '<br /><strong>Phone:</strong> ' + add.mainphone + '';
}
if (!isBlank(add.mainemail)) {
ipop += '<br /><strong>Email:</strong> ' + add.mainemail + '';
}
console.log('HEY NOW: ' + pos.toString() + ' - Location Found!');
var mark = new google.maps.Marker({
title: add.title,
position: pos,
map: map,
icon: image,
html: ipop
});
google.maps.event.addListener(mark, 'click', function () {
iw.setContent(this.html);
iw.open(map, this);
});
}
});
proof of concept fiddle
code snippet:
var geocoder = new google.maps.Geocoder();
var map;
var infoWindow = new google.maps.InfoWindow();
function initialize() {
map = new google.maps.Map(
document.getElementById("map_canvas"), {
center: new google.maps.LatLng(37.4419, -122.1419),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
PlaceMarkers(infoWindow, adds, geocoder);
}
google.maps.event.addDomListener(window, "load", initialize);
// Place Markers on the Map
var PlaceMarkers = function(iw, adds, gc) {
var bounds = new google.maps.LatLngBounds();
var image = {
url: "http://meatmysite.com/Images/star2.png",
size: new google.maps.Size(24, 24)
};
var aCt = adds.length;
for (var i = 0; i < aCt; ++i) {
GetLatLng(gc, adds[i], function(pos, add) {
if (pos) {
var ipop = '<h1>' + add.title + '</h1>'; // <----- TypeError: adds[i] is undefined
if (!isBlank(add.url)) {
ipop += '' + add.url + '<br />';
}
ipop += '<div class="map_item_content" id="mi_content' + i + '">' + add.content + '</div>';
if (!isBlank(add.mainphone)) {
ipop += '<br /><strong>Phone:</strong> ' + add.mainphone + '';
}
if (!isBlank(add.mainemail)) {
ipop += '<br /><strong>Email:</strong> ' + add.mainemail + '';
}
console.log('HEY NOW: ' + pos.toString() + ' - Location Found!');
var mark = new google.maps.Marker({
title: add.title,
position: pos,
map: map,
// icon: image,
html: ipop
});
bounds.extend(mark.getPosition());
map.fitBounds(bounds);
google.maps.event.addListener(mark, 'click', function() {
iw.setContent(this.html);
iw.open(map, this);
});
}
});
}
};
// Get Lat/Lng Location
var GetLatLng = function(gc, add, f) {
gc.geocode({
'address': add.address
}, function(res, status) {
if (status == 'OK') {
f(res[0].geometry.location, add);
}
});
};
var adds = [{
"address": "1 My Street Gilbert, AZ 85234",
"title": "My Title 1",
"url": "http://www.myurl.com/",
"mainphone": null,
"mainemail": null,
"content": "1 My Street<br />Gilbert, AZ 85234"
}, {
"address": "2 My Street North Richland Hills, TX 76182",
"title": "My Title 2",
"url": null,
"mainphone": null,
"mainemail": null,
"content": "2 My Street<br />North Richland Hills, TX 76182"
}];
function isBlank(str) {
return (!str || /^\s*$/.test(str));
}
html,
body,
#map_canvas {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js"></script>
<div id="map_canvas"></div>
This looks like a typical binding issue. By the time your callback is called, the value of adds[i] will have changed. It is likely that the loop terminated and i has now a value of last index + 1, which is pointing to nothing. Note that it could also point to the wrong index, that would not fail but use the wrong data.
You must bind the value of adds[i] locally for each iteration or the callback will just use a reference to a global value. There a multiple ways to go about this, here is a simple one where we keep passing adds[i] along as a function argument.
Replace adds[i].address with adds[i] when calling GetLatLng and add a second parameter add to the callback:
GetLatLng(gc, adds[i], function(pos, add) {
...
});
Then modify GetLatLng to use add.address instead of just add and add add to the callback call:
// Get Lat/Lng Location
var GetLatLng = function(gc, add, f) {
var ret = '';
gc.geocode({'address': add.address}, function(res, status) {
if (status == 'OK') {
f(res[0].geometry.location, add);
console.log('Found Here: ' + ret.toString());
}
});
return -1;
};
Then in the callback function, replace all instances of adds[i] with add to use the local variable.
I didn't set up a test but it should theoretically work.
you appear to be overcomplicating things. Any reason why you can't do this?
// Place Markers on the Map
var PlaceMarkers = function (iw, adds, gc) {
var aCt = adds.length;
for(var i = 0; i < aCt; ++i) {
var obj=adds[i];
GetLatLng(gc, obj)
}
};
// Get Lat/Lng Location
var GetLatLng = function(gc, obj) {
var ret = '';
gc.geocode({'address': obj.address}, function(res, status) {
if (status == 'OK') {
var pos=res[0].geometry.location;
var ipop = '<h1>' + obj.title + '</h1>'; // <----- TypeError: adds[i] is undefined
if(!isBlank(obj.url)){
ipop += '' + obj.url + '<br />';
}
ipop += '<div class="map_item_content" id="mi_content">' + obj.content + '</div>';
if(!isBlank(obj.mainphone)){
ipop += '<br /><strong>Phone:</strong> ' + obj.mainphone + '';
}
if(!isBlank(obj.mainemail)){
ipop += '<br /><strong>Email:</strong> ' + obj.mainemail + '';
}
console.log('HEY NOW: ' + pos.toString() + ' - Location Found!');
var mark = new google.maps.Marker({title: obj.title, position: pos, map: map, html: ipop});
google.maps.event.addListener(mark, 'click', function(){
iw.setContent(this.html);
iw.open(map, this);
});
} else {
console.log("geocoder problem!")
}
});
};
for(var i = 0; i < aCt - 1; ++i). You need to add "-1" in you for-loop. The array starts at index 0 and not 1. You also need to be careful with using functions in a for loop. Within javascript a for-loop does not have a scope from itself. Only functions create new scopes.
Why is the string lost inside the object within a loop?
for (var i = 0; i < nrow.length - 1; i++) {
displayNote = "<b>" + nfield[0] + "</b><br />" + nfield[1] + " " + nfield[2] + "<br /> " + nfield[7];
$('#googleMap').gmap3({
action: 'addMarker',
lat: parseFloat(nfield[5]),
lng: parseFloat(nfield[6]),
events: {
mouseover: function (marker, event) {
var map = $(this).gmap3('get'),
infowindow = $(this).gmap3({ action: 'get', name: 'infowindow' });
if (infowindow) {
infowindow.open(map, marker);
infowindow.setContent(displayNote);
displayNote only displays the first increment for all the other infowindow
at the end of for loop execution displayNote will contain last value. And InfoWindow will show last displayNote on mouseover.
You can save displayNote for each iteration by creating new function
function attachEvent( displayNote, nfield ){
$('#googleMap').gmap3({
action: 'addMarker',
lat: parseFloat(nfield[5]),
lng: parseFloat(nfield[6]),
events: {
mouseover: function (marker, event) {
var map = $(this).gmap3('get'),
infowindow = $(this).gmap3({ action: 'get', name: 'infowindow' });
if (infowindow) {
infowindow.open(map, marker);
infowindow.setContent(displayNote);
}
for (var i = 0; i < nrow.length - 1; i++) {
displayNote = "<b>" + nfield[0] + "</b><br />" + nfield[1] + " " + nfield[2] + "<br /> " + nfield[7];
attachEvent( displayNote, nfield );
}
I am having problems adding GMarkers when using a loop. The best way to explain the problem is to show the code, I guess :)
This works:
htmls[0] = "<div style=\"margin-bottom:10px; \"><table><tr><td><img src=\"" + result[0].UserImageURI + "\" width=\"80\" height=\"80\" /></td><td style=\"vertical-align:top; \"><strong>" + result[0].Username + "</strong> (" + result[1].Age + ")<br/>" + result[0].Country + "<br/>" + result[0].Distance + " KMs away<br/>View Profile</td></tr></table></div>";
latlngs[0] = new GLatLng(result[0].Latitude, result[0].Longitude);
if (result[0].Gender == "F") {
markers[0] = new GMarker(latlngs[0], { draggable: false, icon: fIcon });
} else {
markers[0] = new GMarker(latlngs[0], { draggable: false, icon: mIcon });
}
GEvent.addListener(markers[0], "click", function () {
markers[0].openInfoWindowHtml(htmls[0]);
});
map.addOverlay(markers[0]);
htmls[1] = "<div style=\"margin-bottom:10px; \"><table><tr><td><img src=\"" + result[1].UserImageURI + "\" width=\"80\" height=\"80\" /></td><td style=\"vertical-align:top; \"><strong>" + result[1].Username + "</strong> (" + result[1].Age + ")<br/>" + result[1].Country + "<br/>" + result[1].Distance + " KMs away<br/>View Profile</td></tr></table></div>";
latlngs[1] = new GLatLng(result[1].Latitude, result[1].Longitude);
if (result[1].Gender == "F") {
markers[1] = new GMarker(latlngs[1], { draggable: false, icon: fIcon });
} else {
markers[1] = new GMarker(latlngs[1], { draggable: false, icon: mIcon });
}
GEvent.addListener(markers[1], "click", function () {
markers[1].openInfoWindowHtml(htmls[1]);
});
map.addOverlay(markers[1]);
But when I put it in a loop, it doesn't work...
for (i = 0; i < result.length; i++) {
htmls[i] = "<div style=\"margin-bottom:10px; \"><table><tr><td><img src=\"" + result[i].UserImageURI + "\" width=\"80\" height=\"80\" /></td><td style=\"vertical-align:top; \"><strong>" + result[i].Username + "</strong> (" + result[i].Age + ")<br/>" + result[i].Country + "<br/>" + result[i].Distance + " KMs away<br/>View Profile</td></tr></table></div>";
latlngs[i] = new GLatLng(result[i].Latitude, result[i].Longitude);
if (result[i].Gender == "F") {
markers[i] = new GMarker(latlngs[i], { draggable: false, icon: fIcon });
} else {
markers[i] = new GMarker(latlngs[i], { draggable: false, icon: mIcon });
}
GEvent.addListener(markers[i], "click", function () {
markers[i].openInfoWindowHtml(htmls[i]);
});
map.addOverlay(markers[i]);
}
When using the loop, clicking on a marker breaks the script. It points to the line
markers[i].openInfoWindowHtml(htmls[i]);
And says that object is undefined. It also says that i = 10 at that point which is "impossible" as results.length is only 10
The problem is the classic function-in-a-loop. Here's one of the two typical ways to fix it:
function callback(i) {
return function () {
markers[i].openInfoWindowHtml(htmls[i]);
};
}
for (i = 0; i < result.length; i++) {
// snip...
GEvent.addListener(markers[i], "click", callback(i));
// snip...
}
JSLint can easily catch these common errors.
Edit
#Alex's answer shows roughly the other typical way that this problem is fixed, but with a few errors. This should work, though:
for (i = 0; i < result.length; i++) {
// snip...
GEvent.addListener(markers[i], "click", (function (i) {
return function () {
markers[i].openInfoWindowHtml(htmls[i]);
}
})(i));
// snip...
}
In this piece of code...
GEvent.addListener(markers[i], "click", function () {
markers[i].openInfoWindowHtml(htmls[i]);
});
...the function has closure to the i in the parent scope. So it is accessing the variable itself, not a copy of it.
At the end of the loop, when your function accesses the i variable, it will be equal to whatever condition stopped the loop, 10 in your example.
You can fix it with a self invoking anonymous function which passes the value to a new variable with a limited lifespan...
(function(j) {
GEvent.addListener(markers[j], "click", function () {
markers[j].openInfoWindowHtml(htmls[j]);
});
})(i);
Here is an example of similar code working.