• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1327
  • Last Modified:

Javascript Error - Uncaught TypeError: Object [object global] has no method 'handleApiReady'

With the Google Maps async code below I'm seeing this error:
Uncaught TypeError: Object [object global] has no method 'handleApiReady'

The code runs fine but in the console I see this error.

<script src="http://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=handleApiReady"></script>

 <script type="text/javascript">
   $LAB
  	       .script("//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js")	
				    .script("/js/poiaktuell.js")
		  .script("/js/grey.js")
   .script("/js/jquery.slidetabs.min.js")
   .script("/js/jquery.slidetabs.touch.min.js")
   .script("/js/jquery-litelighter.js")

    .wait(function(){

	function handleApiReady() {

	setupAddress();
	mapDiv = document.getElementById(mapDivID);
	var myLatlng = new google.maps.LatLng(0,0);
	var myOptions = {
		zoom: zoomLevel,
			 styles: gmapGreyStyle,
		scrollwheel: true,
		disableDoubleClickZoom: true,
		center: myLatlng,
		mapTypeControl: true,
		mapTypeControlOptions: {
		  style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,
		  position: google.maps.ControlPosition.BOTTOM_LEFT
		},
		zoomControl: true,
		zoomControlOptions: {
		  style: google.maps.ZoomControlStyle.LARGE,
  		  position: google.maps.ControlPosition.TOP_LEFT
		},
		panControl: false,
		mapTypeId: google.maps.MapTypeId.ROADMAP,
		overviewMapControl: true,
		overviewMapControlOptions: {
			opened: false
			}
	};
	map = new google.maps.Map(mapDiv, myOptions);
	

	if(directionsMode == 'DRIVING') {
		travMode = google.maps.DirectionsTravelMode.DRIVING;
	} else {
		travMode = google.maps.DirectionsTravelMode.WALKING;
	}
	//streetview stuff
	var tempPanorama = map.getStreetView();

	google.maps.event.addListener(tempPanorama, 'visible_changed', function() {
	    if (tempPanorama.getVisible()) {
			controlToggle("hide");
		} else {
			controlToggle("show");
	    }
	});
	
	infoBox();

	var geocoder = new google.maps.Geocoder();
    var address = startAddress;
    geocoder.geocode( { 'address': address}, function(results, status) {
      if (status == google.maps.GeocoderStatus.OK) {
        map.setCenter(results[0].geometry.location);
   		addressSet = 1;
		searchCenter = results[0].geometry.location;
        getCategories(1);
		createMarker(searchCenter, 0, markerHTML, "pin", "pin","");
//		var trafficLayer = new google.maps.TrafficLayer();
//		trafficLayer.setMap(map);
	    google.maps.event.addListener(map, "dragend", function () {
				controlToggle("show");
			    getCategories(0);
	    });
		mobileScroll(sidebarDivID);
		if (autoGeolocation === true) {
					geotarget();
		}
		if (mapExtra === true) {
			mapPost();
		}
		}
    });

}


	$(document).ready(function() {

		  handleApiReady();
		$('pre').litelighter({clone:false, style:'light', language:'js'});
						  		
		// Auto Height
		$('#auto-height').slidetabs({
			autoHeight      : true,
			touchSupport: true,
			contentAnim: 'fadeOutIn',
			contentAnimSpeed: 100,
			autoHeightSpeed : 900
			
			
		});
  	});		
		 });
</script>

Open in new window

0
seopti
Asked:
seopti
  • 4
  • 3
1 Solution
 
Scott Fell, EE MVEDeveloper & EE ModeratorCommented:
I am not familiar with labjs but could it be the order you are loading? http://labjs.com/documentation.php
0
 
Tom BeckCommented:
Never heard of labjs until now but in reading the documentation, you should also be loading the Maps Api using labjs. As is, you are attempting to call handleApiReady as a callback to the loading of the API, despite the fact that the handleApiReady function is out of scope inside the "wait" function, all before the other includes have been loaded:
<script src="http://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=handleApiReady"></script>

This makes more sense to me (untested):
$LAB
               .script("//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js")      
                            .script("/js/poiaktuell.js")
              .script("/js/grey.js")
   .script("/js/jquery.slidetabs.min.js")
   .script("/js/jquery.slidetabs.touch.min.js")
   .script("/js/jquery-litelighter.js")
   .script("//maps.googleapis.com/maps/api/js?v=3.exp&sensor=false")

    .wait(function(){

      //load the map directly without the handleApiReady function wrapper

...
...

Remove the handleApiReady call from the $(document).ready function.
0
 
seoptiAuthor Commented:
Thanks for the answer but

1. The google map always has to be loaded first. (otherwise "error google is not defined")
2. The callback is necessary otherwise it will not load ansychronously.

If you view both files below you can see the two different scripts. The first one contains document.write and is not async, the second is async.

http:////maps.googleapis.com/maps/api/js?v=3.exp&sensor=false
http://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=handleApiReady
0
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

 
seoptiAuthor Commented:
At stackoverflow there is  working jsfiddle example to load it dynamically, but it's a bit too complicated for me.

stackoverflow.com/questions/3922764/load-google-maps-v3-dynamically-with-ajax
http://jsfiddle.net/fZqqW/94/
0
 
Tom BeckCommented:
Here is a mockup page with a map that loads asynchronously with the other includes.

Normally, I would like to call handleApiReady() from inside the $(document).ready function to be sure that the map div is available. I cannot do that when using LABjs because the document is ready before the API is loaded because the API is loading asynchronously so the map will fail. Because I cannot use $(document).ready, I have to define the map in the body AFTER the map div is available.
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Untitled Document</title>
<script src="js/LABjs-2.0.3/LAB.min.js" type="text/javascript"></script>
<script>
$LAB
	.script("http://maps.googleapis.com/maps/api/js?v=3.7&sensor=false&callback=handleApiReady")
	.script("http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js")

	.wait(function() {
		
		$(document).ready(function(){
			console.log("doc ready");
		});

	});	
</script>
</head>
<body>
<div id="map" style="width:300px;height:300px"></div>
<script>
function handleApiReady() {
	var myOptions = {
      	center: new google.maps.LatLng(-34.397, 150.644),
      	zoom: 8,
      	mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    var map = new google.maps.Map(document.getElementById("map"), myOptions);	
}
</script>
</body>
</html>

Open in new window


The $(document).ready function has to be inside the "wait" function or it will be called before the jquery library loads. But it's no good to you for loading the map because the map API is loading on a different thread asynchronously.
0
 
Tom BeckCommented:
The Stackoverflow link shows a Google Maps script written with version 2 of the API. Steer clear of that.
0
 
seoptiAuthor Commented:
Thank you, I've been trying a few solutions and this is just close. There is just one error "Google is not defined" which means  Google Maps script needs to be loaded first. What I don't understand I have loaded it first  and asynchronous in the head and still this error prevents the website javascript from loading.

This is straight from the Google website.

https://developers.google.com/maps/documentation/javascript/examples/map-simple-async

In the head:

<script>

		function loadScript() {
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&' +
      'callback=initialize';
  document.body.appendChild(script);
}
</script>

Open in new window




In the body:

 <script type="text/javascript">
   $LAB
  	       .script("//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js")	
				    .script("/js/poiaktuell.js")
		  .script("/js/grey.js")
   .script("/js/jquery.slidetabs.min.js")
   .script("/js/jquery.slidetabs.touch.min.js")
   .script("/js/jquery-litelighter.js")

    .wait(function(){


	function initialize() {

	setupAddress();
	mapDiv = document.getElementById(mapDivID);
	var myLatlng = new google.maps.LatLng(0,0);
	var myOptions = {
		zoom: zoomLevel,
			 styles: gmapGreyStyle,
		scrollwheel: true,
		disableDoubleClickZoom: true,
		center: myLatlng,
		mapTypeControl: true,
		mapTypeControlOptions: {
		  style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,
		  position: google.maps.ControlPosition.BOTTOM_LEFT
		},
		zoomControl: true,
		zoomControlOptions: {
		  style: google.maps.ZoomControlStyle.LARGE,
  		  position: google.maps.ControlPosition.TOP_LEFT
		},
		panControl: false,
		mapTypeId: google.maps.MapTypeId.ROADMAP,
		overviewMapControl: true,
		overviewMapControlOptions: {
			opened: false
			}
	};
	map = new google.maps.Map(mapDiv, myOptions);
	

	if(directionsMode == 'DRIVING') {
		travMode = google.maps.DirectionsTravelMode.DRIVING;
	} else {
		travMode = google.maps.DirectionsTravelMode.WALKING;
	}
	//streetview stuff
	var tempPanorama = map.getStreetView();

	google.maps.event.addListener(tempPanorama, 'visible_changed', function() {
	    if (tempPanorama.getVisible()) {
			controlToggle("hide");
		} else {
			controlToggle("show");
	    }
	});
	
	infoBox();

	var geocoder = new google.maps.Geocoder();
    var address = startAddress;
    geocoder.geocode( { 'address': address}, function(results, status) {
      if (status == google.maps.GeocoderStatus.OK) {
        map.setCenter(results[0].geometry.location);
   		addressSet = 1;
		searchCenter = results[0].geometry.location;
        getCategories(1);
		createMarker(searchCenter, 0, markerHTML, "pin", "pin","");
//		var trafficLayer = new google.maps.TrafficLayer();
//		trafficLayer.setMap(map);
	    google.maps.event.addListener(map, "dragend", function () {
				controlToggle("show");
			    getCategories(0);
	    });
		mobileScroll(sidebarDivID);
		if (autoGeolocation === true) {
					geotarget();
		}
		if (mapExtra === true) {
			mapPost();
		}
		}
    });

}


	$(document).ready(function() {

		$('pre').litelighter({clone:false, style:'light', language:'js'});
						  		
		// Auto Height
		$('#auto-height').slidetabs({
			autoHeight      : true,
			touchSupport: true,
			contentAnim: 'fadeOutIn',
			contentAnimSpeed: 100,
			autoHeightSpeed : 900
			
			
		});
  	});		
		 });
</script>

Open in new window

0
 
Tom BeckCommented:
Where are you calling loadScript()?

You should have either:

window.onload = loadScript;

in a javascript block somewhere or:

<body onLoad="loadScript()">

in the body tag. If you are not calling loadScript() you will get "Google is undefined".

But then, why are you not using my suggestion if you really want the map to load asynchronously? The way you have it, the Maps API is loading synchronously with the LABjs stuff because it's outside of the LABjs stuff. The initialize() function is inside the LABjs "wait" function which means that it will not begin to run until all the includes are loaded. No point in that. The other includes have nothing to do with the map. You might as well run initialize() as soon as the API is ready. Take the initialize function out of the "wait" function and paste it somewhere in the body after the map div. Load the Maps API using LABjs along with the other includes. The callback=handleApiReady will execute the handleApiReady function as soon as the API is ready WHILE the other includes are loading.

Also, putting this:
<script>

		function loadScript() {
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&' +
      'callback=initialize';
  document.body.appendChild(script);
}
</script>

Open in new window

...inside the head tags has the same affect as putting this:

<script type="text/javascript"  src="http://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=handleApiReady"></script>

...inside the body tags. Use the LABjs instead so the map is truly loading asynchronously and use it in the head section. That's where includes normally go. The Google maps sample code you cite is ignorant of your use of LABjs so it does not apply here. The only important thing in that example is the use of "callback=" to load the map when the API is ready.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Cloud Class® Course: Amazon Web Services - Basic

Are you thinking about creating an Amazon Web Services account for your business? Not sure where to start? In this course you’ll get an overview of the history of AWS and take a tour of their user interface.

  • 4
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now