Preload Google Maps – jQuery Mobile


Loading a Google Map once page is shown may take some time. It will take longer if you are using Markers and custom Icons. If you are dealing with many maps, and don’t want user to wait for a map to load, the only solution is to load map(s) in background.

Since jQuery Mobile uses Ajax in loading different pages and/or external pages, you can load map(s) somewhere out the viewport. If you think the answer is “display: none;“, you will end up getting an unresponsive map once it is shown.

Easy, isn’t? Well, it sounds easy but…let’s see.

The challenge here isn’t preloading maps but rather setting correct and accurate dimensions to map(s) upon loading them, and when moving them to content div of a page. The job is easier when map page has no contents but the map. However, when that page has toolbars or other contents inside content div, things will get a bit tougher yet FUN!

First step:

Create a placeholder for each map and position it out of viewport…far, far away. Inside that placeholder add map canvas div with a unique id. Also, remove content div padding.

<div id="mapPH">
  <div id="map-canvas"></div>
</div>
#mapPH {
  position: absolute;
  top:-999;
  z-index: -9999;
}

.ui-content {
  padding: 0;
}

Second step:

Before loading any map, we need to set its’ dimensions based on page‘s contents and toolbars, hence we are going to use the same method in this article with some tweaks. The below function will reset map canvas‘s height and width, it should be called 1) before loading a map, 2) when moving it from placeholder to content div, 3) and when page is resized or orientation is changed. Note that canvas in canvasHeight(canvas) is the id of the map canvas.

function canvasHeight(canvas) {
    var canvasPage = $(canvas).closest("[data-role=page]").length !== 0 ? $(canvas).closest("[data-role=page]") : $(".ui-page").first(),
        screen = $.mobile.getScreenHeight(),
        header = $(".ui-header", canvasPage).hasClass("ui-header-fixed") ? $(".ui-header", canvasPage).outerHeight() - 1 : $(".ui-header", canvasPage).outerHeight(),
        footer = $(".ui-footer", canvasPage).hasClass("ui-footer-fixed") ? $(".ui-footer", canvasPage).outerHeight() - 1 : $(".ui-footer", canvasPage).outerHeight(),
        newHeight = screen - header - footer;
    $(canvas).height(newHeight);
    $(canvas).width($(window).width());
}

Load map on any jQuery Mobile’s page events. I have used pagecontainershow in this example, you are free to use any of them. Nevertheless, we need to load it once only, so we have to bind it once to page event using .one().

We will call loadMap(canvas) only once on page event i.e. pagecontainershow. Inside that function, there is another important step, which is moving the map from placeholder to its’ destination once loaded and removing placeholder. It is already embedded within loadMap(canvas), this is just for your info.

google.maps.event.addListenerOnce(mapData.map, 'idle', function (e) {
  $("#map .ui-content").append($("#mapPH #map-canvas"));
  $("#mapPH").remove();
});

In this article, I have used an object as a variable to store map data, in order to access it from any function. I have read somewhere it is better than using Global Variables, not our issue anyway.

var mapData = {};

function loadMap(canvas) {
    var mapOptions = {
        center: new google.maps.LatLng(36.4875, -4.9525),
        zoom: 16,
        mapTypeId: google.maps.MapTypeId.HYBRID,
        mapTypeControl: false,
        streetViewControl: false,
        zoomControlOptions: {
            position: google.maps.ControlPosition.RIGHT_BOTTOM
        }
    };
    mapData.map = new google.maps.Map($(canvas)[0],
    mapOptions);
    google.maps.event.addListenerOnce(mapData.map, 'idle', function (e) {
        $("#map .ui-content").append($("#mapPH #map-canvas"));
        $("#mapPH").remove();
    });
}

Third step:

The setup is now ready, all functions are good to go! However, there is tiny issue here..content div’s dimensions are not defined nor toolbar, if map page isn’t created yet. In case map page has no contents nor toolbars whatsoever, the job is a piece of cake! Set map canvas dimensions based on viewport‘s dimensions to fill the whole available space.

No contents?

$("#map-canvas").height( $(window).height() );
$("#map-canvas").width( $(window).width() );

Anyway, back to map loading. Once first page is shown, the map will be loaded and its’ dimensions will be defined. When moving from any page to map page, we need to reset map’s dimensions again to fit the available space. Simply, run canvasHeight(canvas) upon clicking map page link button. Again, this is in case you contents and toolbars, otherwise, the code above is enough to reset map canvas‘s dimensions.

$(document).one("pagecontainershow", function () {
    canvasHeight("#map-canvas");
    loadMap("#map-canvas");
    $(".showMap").one("click", function () {
        setTimeout(function () {
            canvasHeight("#map-canvas");
        }, 500);
    });
});

All is well! Map is loaded, re-positioned and shown! What if the user changes orientation or resize the window? Call canvasHeight(canvas) or the simple “No contents” code.

$(window).on("throttledresize orientationchange", function () {
    canvasHeight("#map-canvas");
});

The End.

Demo – With toolbar
Demo – Fullscreen

Advertisements

Drop me a line or two ;)

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s