Link to home
Start Free TrialLog in
Avatar of seopti
seopti

asked on

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

Avatar of Scott Fell
Scott Fell
Flag of United States of America image

I am not familiar with labjs but could it be the order you are loading? http://labjs.com/documentation.php
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.
Avatar of seopti
seopti

ASKER

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
Avatar of seopti

ASKER

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/
ASKER CERTIFIED SOLUTION
Avatar of Tom Beck
Tom Beck
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
The Stackoverflow link shows a Google Maps script written with version 2 of the API. Steer clear of that.
Avatar of seopti

ASKER

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

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.