1

Phonegap app with jQuery Mobile 1.4 and Google Maps JavaScript API v3.

The map takes a couple seconds to load on a mobile device, sometimes longer. I'm trying to "preload" the map page so it appears instant when the user navigates to the page.

Some things I've tried unsuccessfully:

$.mobile.pageContainer.enhanceWithin();to create pages on app load. It creates the map, but no map display unless resize triggered on pageshow.

Creating map on app load, then triggering a map resize on pageshow, so it refreshes map size. Maybe a bit faster, but still need to wait, as original map not rendered correct size, so you need to resize.

Manually set css height/width of the map container and load map on app startup. Correct container size, but still need to trigger resize.

Optimal solution is to load the full map page (plus some extra tiles in case they scroll) in the background on app load, then when user navigates to it, it's already propagated.

Joe's Ideas
  • 492
  • 6
  • 22
  • Recommend profiling the app with Safari DevTools and XCode. The delay could be related to loading the Google Maps API over the network. Or it could be something completely different. – Steve Jansen Feb 11 '14 at 10:20

1 Answers1

2

You can achieve this by loading map canvas once first page is fully loaded and shown. However, map canvas should be placed in a div that isn't visible in current viewport. Using display: none; on map canvas will break the structure and becomes unresponsive when you move it from div to another.

Another important note, height of content div are always 0 before and after creating a page. Hence, content div's height should be predefined in order to accommodate map canvas after it is loaded in background.

  • First step:

    Create a map placeholder outside any page div, and make sure it isn't within viewport view by changing it's position CSS property. Inside map placeholder, add map canvas div. Moreover, remove content div padding to fill the whole available area.

    • HTML

      <div id="mapPH">
        <div id="map-canvas"></div>
      </div>
      
    • CSS

      #mapPH {
        position: absolute;
        top:-999;
        z-index: -9999;
      }
      
      .ui-content {
        padding: 0;
      }
      
  • Second step:

    On any jQM's page events, load map into map canvas ONLY one time .one(). Once it fully loaded, move to its' new parent. You can either move it automatically once loaded by listening to tilesloaded event, or move it once to change to map page.

    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
        }
    };
    var map = new google.maps.Map($(canvas)[0],
    mapOptions);
    var marker = new google.maps.Marker({
        position: new google.maps.LatLng(36.4875, -4.9525),
        map: map,
        title: 'Hello World!'
    });
    
    /* move map once fully loaded
       by listening to "tilesloaded".
       Remove "placeholder" after moving the  map */
    google.maps.event.addListener(map, 'tilesloaded', function () {
        $("#map .ui-content").append($("#mapPH #map-canvas"));
        $("#mapPH").remove();
    });
    
    /* or choose any other method to move it */
    $(".selector").on("click", function () {
      $("#map .ui-content").append($("#mapPH #map-canvas"));
      $("#mapPH").remove();
    });
    
  • Third step:

    Here comes the tricky part, map canvas's dimensions (width & height). As I've mentioned before, content div's height should either predefined manually or dynamically using the below function.

    function canvasHeight(canvas) { /* canvas = map's canvas ID */
        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());
    }
    

    That function should be called once map canvas is loaded as well as when moving it. However, to get actual dimensions, a setTimeout() is required here, since the page you're moving to is still not created. When a page isn't created, you won't get actual/true height of elements within that page.

    Update: map canvas's dimensions should be updated on two events, throttledresize and orientationchange.

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

Here is the complete code for more clarification.

/* map canvas dimensions */
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());
}

/* map canvas loading */
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
        }
    };
    var map = new google.maps.Map($(canvas)[0],
    mapOptions);
    var marker = new google.maps.Marker({
        position: new google.maps.LatLng(36.4875, -4.9525),
        map: map,
        title: 'Hello World!'
    });
    /* option 1: moving map once loaded */ 
    google.maps.event.addListener(map, 'tilesloaded', function () {
        $("#map .ui-content").append($("#mapPH #map-canvas"));
        $("#mapPH").remove();
    });
}

/* load map in background
   once first page is shown 
   and update its' dimensions */
$(document).one("pagecontainershow", function () {
    canvasHeight("#map-canvas");
    loadMap("#map-canvas");

    /* option 2: move map on "click" 
       and update its' dimensions */
    $(".showMap").one("click", function () {
        $("#map .ui-content").append($("#mapPH #map-canvas"));
        setTimeout(function () {
            canvasHeight("#map-canvas");
        }, 500);
        $("#mapPH").remove();
    });
});

/* update map canvas dimensions
   when window is resized and changing orientation */
$(window).on("throttledresize orientationchange", function () {
  canvasHeight("#map-canvas");
});

Demo

Omar
  • 32,302
  • 9
  • 69
  • 112