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

JavaScript

Avatar of undefined
Last Comment
Tom Beck

8/22/2022 - Mon
Scott Fell

I am not familiar with labjs but could it be the order you are loading? http://labjs.com/documentation.php
Tom Beck

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.
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
All of life is about relationships, and EE has made a viirtual community a real community. It lifts everyone's boat
William Peck
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
Tom Beck

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
Tom Beck

The Stackoverflow link shows a Google Maps script written with version 2 of the API. Steer clear of that.
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

⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
Tom Beck

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.