How to dynamically load map APIs

When using various map APIs it’s often useful for performance reasons not to actually load the API until we’re sure we’re going to need it. This is called dynamic loading or lazy loading. The post explains how to do this on the three major mapping APIs.

Note about the examples

These have been excerpted from a project I am currently working on, and so don’t work as-is. The basic calling structure (not shown) is as follows:

  • assume each function is being called from CALLER.
  • If true is returned we allow the dynamic loader to do it’s work; when that is complete it calls CALLER.CREATE_MAP.
  • If true isn’t returned, we assume everything is good to go and the caller directly calls CALLER.CREATE_MAP – this will typically happen when the Map API is detected in Javascript!

There’s also a global context in GLOBALS used for keeping track of state. In particular, we assume the user may be trying to create multiple maps so we have to make sure all the CREATE_MAP callbacks are triggered once all the Javascript has been loaded!

In the Google Maps and Virtual Earth we see the Javascript-with-callback pattern:

  • a callback function is passed in the URL as a parameter
  • the downloaded Javascript document is dynamically modified so that it calls that function. Hence, you know the Javascript has been loaded.

If you’re a Javascript noob, scripts are dynamically loaded into your HTML document by creating a SCRIPT element and attaching it to the document HEAD.

Google Maps API

Google Maps API uses a multi-phase loading scheme:

  • get the generic loader, giving it a callback function to invoke when it’s completely loaded
  • when that’s loaded (into google)…
  • call the loader to get the maps API
  • when that’s loaded, create the map

This feature is documented here.

google_preload : function(CALLER) {

if (window.GMap2) {

return;

}

var api_key = YOUR_GOOGLE_API_KEY_FOR_YOUR_DOMAIN;

var preloader = 'MapGooglePreloader_' + GLOBALS.MapFieldsNumber;

GLOBALS[preloader] = function() {

google.load("maps", "2", {

"callback" : function() {

CALLER.CREATE_MAP();

}

});

}

if (window.google) {

GLOBALS[preloader]();

} else {

var script = document.createElement("script");

script.src = "http://www.google.com/jsapi?key=" + api_key +

"&callback=GLOBALS." + preloader;

script.type = "text/javascript";

document.getElementsByTagName("head")[0].appendChild(script);

}

return true;

}

Microsoft Virtual Earth

The Virtual Earth API is easy to dynamically load also. As with Google this feature is directly supported through a Javascript callback mechanism; unlike Google it’s essentially a single phase design.

One issue I ran into was the error message p_elSource.attachEvent is not a function error. Although a Google search for a solution mostly turns up a blank, a tip of the hat to Soul Solutions in Australia for finding a work-around (and have similarly solved dynamic API loading at the link).

virtualearth_preload : function(CALLER) {

if (window.VEMap) {

return;

}

var preloader = 'MapVEPreloader_' + GLOBALS.MapFieldsNumber;

GLOBALS[preloader] = function() {

CALLER.CREATE_MAP();

}

if (!window.attachEvent) {

var script = document.createElement("script");

script.src = "http://dev.virtualearth.net/mapcontrol/v6.2/js/atlascompat.js";

script.type = "text/javascript";

document.getElementsByTagName("head")[0].appendChild(script);

}

var script = document.createElement("script");

script.src = "http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2" +

"&onScriptLoad=GLOBALS." + preloader;

script.type = "text/javascript";

document.getElementsByTagName("head")[0].appendChild(script);

return true;

}

Yahoo AJAX Map API

The Yahoo AJAX Map API does not directly support dynamic API loading. However, just loading the Javascript and running a timer that waits to see whether the script has loaded seems to do the trick. If you’re new-ish to Javascript, remember that it’s not multi-threaded so we can always be sure that when a Javascript file is loaded, it is completely loaded (excepting errors, of course).

yahoo_preload : function(CALLER) {

if (window.YMap) {

return;

}

var preloader = 'MapYahooPreloader_' + GLOBALS.MapFieldsNumber;

if (!GLOBALS[preloader]) {

GLOBALS[preloader] = 1;

window.YMAPPID = YOUR_YAHOO_API_KEY;

Page 1 of 2 | Next page