AJAX call in an Infowindow: Scope Issue - javascript

Or at least I believe it's a scope issue, correct me if I'm wrong.
I have a for loop that generates markers on my map. Each infowindow loads different content using callbacks to an ajax function.
I've simplified this sample to outline the problem.
var xhr = "";
var infowindow = new google.maps.InfoWindow();
var marker, i;
var polylineCoordinates = [new google.maps.LatLng(78.782762, 17.917843),
new google.maps.LatLng(-0.829439, -91.112473),
new google.maps.LatLng(15.066156, -23.621399),
]
function createHttpRequest() {
try {
xhr = new XMLHttpRequest();
return xhr;
}
catch (e)
{
//assume IE6
try {
xhr = new activeXBbject("microsoft.XMLHTTP");
return xhr;
}
catch (e) {
return alert("Unable to create an XMLHttpRequest object");
}
}
}
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(78.782762,17.917843),
zoom: 10,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"),
mapOptions);
}
//I recreated the polylineCoordinates array (see above)
//to try and replicate and real array in the script
for (i = 0; i < polylineCoordinates.length; i++) {
marker = new google.maps.Marker({
position: polylineCoordinates[i],
map: map
});
google.maps.event.addListener(marker, 'click', (function (marker, i) {
return function () {
infowindow.setContent("<div id=\"infowindow\">" + getStationInfo(infoWindowDiv) + "</div>");
infowindow.open(map, marker);
}
})(marker, i));
} //End adding markers loop
function infoWindowDiv(stationInfo) {
var add = document.createTextNode(stationInfo);
document.getElementById("infowindow").appendChild(add);
}
function getStationInfo(callback) {
//createHttpRequest() exists globally
var xhr = createHttpRequest();
var url = "stations.php" //edited out the original URL
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
var stationInfo = "This is a Test";
return callback(stationInfo)
} //end readyState
} //end readystatechange
xhr.open("GET", url, true);
xhr.send(null);
} //end getStationInfo
Small Edit: Moved functions outside of the loop
Edit 2: There is nothing wrong with the ajax call, the url was edited for the sake of the sample code. Notice the final output shows "This is a test" in the infowindow which clearly states a successful callback was performed. Moreover, notice there is no responseText or responseXml. The variable being sent back has nothing to do with the url
The callback works fine but for some reason it's topped with the dreadful 'undefined' on top of it.
Console shows nothing.
Output:
undefined
This is a test
What am I doing wrong? How can it be undefined if it works?

What is happening:
you click on the infowindow
getStationInfo(infoWindowDiv) is called, fires off an AJAX request, but returns nothing useful ("undefined", there is no return statement)
The AJAX function will encounter an error (url "Unnecessary at this point" will not cause the onreadystatechange function to fire). But you tell us that isn't a problem.
The script encounters the javascript error Uncaught TypeError: Cannot call method 'appendChild' of null because the div with id infowindow hasn't been attached to the DOM.
Suggest adding an event listener on the infowindow to not attempt to access the div with id="infowindow" until it has been rendered (domready).
Working code:
var xhr = "";
var infowindow = new google.maps.InfoWindow();
var map = null;
var marker, i;
var polylineCoordinates = [new google.maps.LatLng(78.782762, 17.917843),
new google.maps.LatLng(-0.829439, -91.112473),
new google.maps.LatLng(15.066156, -23.621399)
]
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(78.782762,17.917843),
zoom: 10,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"),
mapOptions);
for (i = 0; i < polylineCoordinates.length; i++) {
marker = new google.maps.Marker({
position: polylineCoordinates[i],
map: map
});
google.maps.event.addListener(marker, 'click', (function (marker, i) {
return function () {
infowindow.setContent("<div id=\"infowindow\" style=\"height:50px;width:200px;\"></div>");
infowindow.open(map, marker);
google.maps.event.addListenerOnce(infowindow,"domready", function(){
getStationInfo(infoWindowDiv);
});
})(marker, i));
} //End adding markers loop
}
function infoWindowDiv(stationInfo) {
var add = document.createTextNode(stationInfo);
document.getElementById("infowindow").appendChild(add);
}
function getStationInfo(callback) {
var stationInfo = "This is a Test";
callback(stationInfo)
} //end getStationInfo
google.maps.event.addDomListener(window, 'load', initialize);

Related

Call function for content of infowindow when opening not only on init

I have some objects with markers. These objects have dynamic data and I would like to output them in the infowindow of the marker of the object.
So far this is what I have:
function createRandomObjectWithAMarker() {
var marker;
var aData = "dataToDisplay";
var infowindow = new google.maps.InfoWindow({
content:callThisFunctionWhenOpenWindow(aData)
});
marker.addListener('click', function() {
infowindow.open(map, marker);
});
randomObject = {
/*
someData
*/
marker: marker
};
return randomObject;
}
And I would like that this function to be called when I click the marker to show the return modifiedData as the content of the infoWindow.
function callThisFunctionWhenOpenWindow(aData){
/*
do some stuff on aData
*/
return modifiedData;
}
But actually, this function is called once: only when I init the randomObject with the call of createRandomObjectWithAMarker(). So if I show the infoWindow after some time, when the datas would not be the same as when the script starter, it will still display the same output.
Is there any way to do that? If yes how?
Try something like this?
This way the infowindow (and it's data) is only created when you click the marker
function createRandomObjectWithAMarker() {
var marker;
var aData = "dataToDisplay";
marker.addListener('click', function() {
var infowindow = new google.maps.InfoWindow({
content:callThisFunctionWhenOpenWindow(aData)
});
infowindow.open(map, marker);
});
randomObject = {
/*
someData
*/
marker: marker
};
return randomObject;
}

Update marker in Leaflet

I have a problem with a marker in leaflet. My code is this:
var updateMarker = function(lat, lng) {
if($('.leaflet-marker-icon').length)
marker.setLatLng([lat, lng]);
else
var marker = L.marker([lat, lng]).addTo(map);
return false;
};
var updateMarkerByInputs = function() {
return updateMarker( $('#latInput').val() , $('#lngInput').val());
}
$('#latInput').on('input', updateMarkerByInputs);
$('#lngInput').on('input', updateMarkerByInputs);
map.on('click', function(e) {
$('#latInput').val(e.latlng.lat);
$('#lngInput').val(e.latlng.lng);
updateMarker(e.latlng.lat, e.latlng.lng);
});
As you can see, on the first click will be add a marker and in the next clicks it should be updated. But I get this error at the second click:
TypeError: i is undefined
..."_leaflet_id";return function(i){return i[e]=i[e]||++t,i[e]}}(),invokeEach:funct...
leaflet.js (line 6, col 603)
What do I wrong?
You are defining your marker variable locally in the updateMarker function, so the reference is lost as soon as the function returns.
Define it outside the function:
var marker;
var updateMarker = function(lat, lng) {
if($('.leaflet-marker-icon').length)
marker.setLatLng([lat, lng]);
else
marker = L.marker([lat, lng]).addTo(map);
return false;
};
This way you don't need to check the DOM with jQuery to find out if your marker is defined, you can check directly the marker variable:
var marker;
var updateMarker = function(lat, lng) {
if (marker) {
marker.setLatLng([lat, lng]);
} else {
marker = L.marker([lat, lng]).addTo(map);
}
return false;
};
(to improve readability I would suggest to always use brackets in your if statements.)

Google Maps global object; js generated by coffeescript not working

What is the global object in a Google Maps app?
I rewrote the js at https://developers.google.com/maps/documentation/javascript/examples/map-geolocation
in coffeescript, which generated the following javascript:
// Generated by CoffeeScript 1.6.3
(function() {
var errorFlag, initialize;
google.maps.visualRefresh = true;
initialize = function() {
var map, mapOptions;
mapOptions = {
zoom: 15,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
if (navigator.geolocation) {
return navigator.geolocation.getCurrentPosition(function(position) {
var infowindow, pos;
pos = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
infowindow = new google.maps.InfoWindow({
map: map,
position: pos,
content: 'Location found'
});
return map.setCenter(pos);
}, function() {
return handleNoGeolocation(true);
});
} else {
return handleNoGeolocation(false);
}
};
handleNoGeolocation(errorFlag = function() {
var content, infowindow, options;
if (errorFlag) {
content = 'Geolocation failed';
} else {
content = 'Your browser does not support Geolocation';
}
options = {
map: map,
position: new google.maps.LatLng(60, 105),
content: content
};
infowindow = new google.maps.InfoWindow(options);
return map.setCenter(options.position);
});
google.maps.event.addDomListener(window, 'load', initialize);
}).call(this);
The app works when I use the js from their website, but not when I use the js generated by coffeescript. My guess is that since the map variable is a global variable in their code, I should bind it to the global object as well. I tried window.map, but that didn't work either. Any ideas?
The handleNoGeolocation function defination is wrongly compiled. It should have been
var errorFlag, initialize, handleNoGeolocation;
//..
//..
handleNoGeolocation = function (errorFlag) {
//..
//..
};
rather than
handleNoGeolocation(errorFlag = function() {
//..
//..
});
I guess something went wrong with indentation.
hope this helps.

Javascript events firing in undesired order

The problem:
mouseout event fires too early and causes the infobox to stay open when the user drags their mouse over the marker too fast. In other words... If the user quickly moves in and out of the marker... the mouseover event fires, then the mouseout event fires ... but then, the placesService callback is executed and ib.open() is called.
The code:
map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
var request = {
key: 'for_my_eyes_only',
location: new google.maps.LatLng(some_lat, some_lng);,
radius: '500',
types: ["restaurant"]
};
var service = new google.maps.places.PlacesService(map);
service.search(request, placesCallback);
function placesCallback(results, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
var place = results[i];
var marker = new google.maps.Marker({
map: map,
position: place.geometry.location,
});
//Infobox settings
var ib = new InfoBox({
//a bunch of irrelevant properties.
});
google.maps.event.addListener(marker, 'mouseover', markerMouseOverFactory(place, marker, ib));
google.maps.event.addListener(marker, 'mouseout', markerMouseOutFactory(ib));
}
}
}
function markerMouseOverFactory(place, marker, ib){
return function(){
var detailService = new google.maps.places.PlacesService(map);
detailService.getDetails({reference: place.reference}, function(details, status){
if (status == google.maps.places.PlacesServiceStatus.OK) {
ib.setContent(/*set some content using details*/);
ib.open(map, marker);
}
});
}
}
function markerMouseOutFactory(ib){
return function(){
ib.close();
}
}
The question:
Is there a way to abandon a google maps AJAX request? If I could abandon the AJAX request in the mouseout listener, all would be good. Or, how would you solve this? I tried using a simple flag in the mouseout, but couldn't get it working.
There is no method to cancel the request in the API as far as I know. But what I would do is to delay the callback executed inside the mouseover event. So that if the user holds the mouse longer than a specified time; it would mean he/she wants the infobox displayed.
Workaround:
var delayTimer, delay = 800; //less than 1 sec. delay
google.maps.event.addListener(marker, 'mouseover', function() {
delayTimer = setTimeout(function() {
markerMouseOverFactory(place, marker, ib);
}, delay);
});
google.maps.event.addListener(marker, 'mouseout', markerMouseOutFactory(ib));
function markerMouseOutFactory(ib){
clearTimeout(delayTimer); //clear timeout here
return function(){
ib.close();
}
}

How can I add multiple functions to one file?

I'm trying to create a mash Up of sorts... I want the functions to be in one file but when I add my Ajax functions (half way down ) nothing displays.
Also I want to display them with jQuery, and the top function(Google maps with marker and info) all works a treat until I add the bottom functions.
Should I add them in the (function () {} ) like Google has and what is the (); on the end of the googlemap function?
and when I call my functions in my code how will I call the ajax for the preview as the window.onload has been called in the Google one.
I know that I can use the $.ready function(){} but do I just put the function names in the .ready function { }
I am unsure how to add all the functions in one file and make them work. Basically
this is the code:
(function() {
//define global variables
var map, geocoder, marker, infowindow;
window.onload = function() {
//creating the map
var options = {
zoom: 5,
center: new google.maps.LatLng(53.383, -1.483),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById('map'), options);
//code for catching the form submit event goes here
//Getting the reference to the HTML form
var form = document.getElementById('addressForm');
//Catching the forms submit event
form.onsubmit = function () {
//getting the address from the text input
var address = document.getElementById('address').value;
//Making the geocode call
getAddress(address);
//Preventing the form from doing a page submit
return false;
}
}
//Function Stub
function getAddress(address) {
//Check to see if we already have a geocode object.
//If not we create one
if(!geocoder) {
geocoder = new google.maps.Geocoder();
}
//Creating the geoCoderRequest Object
var geocoderRequest = {
address: address
}
//Making the geocode request
geocoder.geocode(geocoderRequest, function (results, status) {
//Check if status is ok beofre proceeding
if (status == google.maps.GeocoderStatus.OK){
//Center the map on the returned location
map.setCenter(results[0].geometry.location);
//Check to see if already a Marker there
if (!marker){
//Create a new marker and add it to the map
marker = new google.maps.Marker({
map: map
});
}
//Setting position of the Marker to returned location
marker.setPosition(results[0].geometry.location);
//Check to see if we've already an info window
if(!infowindow) {
//Creating a new info window
infowindow = new google.maps.InfoWindow();
}
//Creating the content of the info window to the Address
//and the returned position
var content = '<strong>' + results[0].formatted_address + '</strong><br />';
content += 'Lat: ' + results[0].geometry.location.lat() + '<br />';
content += 'Lng: ' + results[0].geometry.location.lng();
//Adding the content to the info window
infowindow.setContent(content);
//Opening the infoWindow
infowindow.open(map, marker);
}
});
}
})();
// beginning of new function
var xhr = false;
var xPos, yPos;
function prev(){
var link = document.getElementByTagName("a").onmouseover = showPreview;
}
function showPreview(evt) {
if (evt) {
var url = evt.target;
}
else{
evt = window.event;
var url = evt.srcElement;
}
xPos = evt.clientX;
yPos = evt.clientY;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
}
else {
if (window.ActiveXObject) {
try {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) { }
}
}
if (xhr) {
xhr.onreadystatechange = showContents;
xhr.open("GET", url, true);
xhr.send(null);
}
else {
alert("Sorry, but I couldn't create an XMLHttpRequest");
}
return false;
}
function showContents() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
var outMsg = xhr.responseText;
}
else {
var outMsg = "There was a problem with the request " + xhr.status;
}
var preview = document.getElementById('preview');
preview.innerHTML = outMsg;
preview.style.top = parseInt(yPos)+2 + "px";
preview.style.left = parseInt(xPos)+2 + "px";
preview.style.visibility = "visible";
preview.onmouseout = function(){
document.getElementById('preview').style.visibility = "hidden";
}
}
It depends on why you're adding functions. but here is a simple formula. If you want you're functions to be called on document ready only and want them to be called once when the document is loaded. Then you add them as "anonymous functions"
EXAMPLE:
$(function () {
//you code
...............
// you can call your named functions also here.
//like
somefunction();
});
But if you expect them to be called later on as well, when the document has already been loaded. Then add the "named functions"
EXAMPLE:
function somename()
{
............
}
In both cases you can have them in one file and regarding the (); at the end of the function, it is a way of calling anonymous functions immediately in JavaScript, like document.ready in jQuery.

Categories

Resources