9

I'm using Jquery Mobile 1.0 and Google Maps v3 to load a user location map. The map loads fine when accessed via direct url but when accessed from a link, it chokes up and nothing is displayed. If I refresh the page, the map loads fine.

Here's a link to a test build that mimics the issue: http://stacefelder.com/stacefelder/Tests/index.html click 'My Map' to see ... nothing. Hit refresh to see the map load.

Here's the script in the map page header:

<script type="text/javascript">
    $('#basic_map').live("pageshow", function() {
        if(navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(function(position){
                initialize(position.coords.latitude,position.coords.longitude);
            });
        }
    });
    function initialize(lat,lng) {
        var latlng = new google.maps.LatLng(lat, lng);
        var myOptions = {
            zoom: 8,
            center: latlng,
            mapTypeId: google.maps.MapTypeId.ROADMAP
        };
        var map = new google.maps.Map(document.getElementById("map_canvas"),myOptions);
    }
</script>

And here's the html in the map page body:

<div data-role="page" id="basic_map">

    <div data-role="header">
        <h1>map test 901</h1>
    </div>
    <div id="map_canvas"></div>
</div>

I've also tried pagecreate instead of pageshow with no discernable difference. Any idea what I'm missing here? Thanks for any and all suggestions!

Stace
  • 147
  • 1
  • 2
  • 9

6 Answers6

14

If you navigate to this page from another page, jQM only pulls in the JS within your div[data-role="page"] tags, so if your JS is in your <head> tags that won't be pulled in, this is what is causing your issues.

Also yes you should use pageinit instead of pageshow, pageshow will re-fire if you navigate back to a page etc... if you have issues with a page being loaded more than once and multiple pages with the same id, there's a clever trick to use the last div[data-role="page"]

jQuery("div:jqmData(role='page'):last").bind('pageinit', function(){


So for a trouble free answer try this:

<div data-role="page" id="basic_map">
    <script type="text/javascript">
        $("div:jqmData(role='page'):last").bind('pageinit', function() {
            if(navigator.geolocation) {
                navigator.geolocation.getCurrentPosition(function(position){
                    initialize(position.coords.latitude,position.coords.longitude);
                });
            }
        });
        function initialize(lat,lng) {
            var latlng = new google.maps.LatLng(lat, lng);
            var myOptions = {
                zoom: 8,
                center: latlng,
                mapTypeId: google.maps.MapTypeId.ROADMAP
            };
            var map = new google.maps.Map(document.getElementById("map_canvas"),myOptions);
        }
    </script>
    <div data-role="header">
        <h1>map test 901</h1>
    </div>
    <div id="map_canvas"></div>
</div>
Clarence Liu
  • 3,874
  • 2
  • 25
  • 29
  • Thanks for the explanation and it makes sense but I'm still getting the same behavior. Your sample code is live at http://www.stacefelder.com/stacefelder/Tests/index.html. Any other ideas? This is making me nuts! – Stace Feb 10 '12 at 17:05
  • Your map is there, you just have no height set on it haha. When you link to the page, inspect the blank area, your map is right there in the DOM. Looking at your HTML source, in your maptest901.php file you declared `#map_canvas { height: 90%; width:100%;}` BUT that's not loaded, if you remember jQM only pulls in whatever is in your div[data-role="page"]. So either put that CSS within that div, or just declare that class in the index page? – Clarence Liu Feb 10 '12 at 17:34
  • Haha - you're right! Thank you very much. It's always the little things. So problem solved in the test build but not in the pre-production model which is a mobile site based on Wordpress and JQM. When I transferred the updated code over there, it doesn't work. When I copy the source of the problem page back to my test build, it works. Any thoughts on what Wordpress might be adding to the mix? – Stace Feb 11 '12 at 00:16
  • I'm sure you're using a mobile plugin for Wordpress right? And you're trying to add jQM code? Does Wordpress's mobile plugin use jQM? If not your pageinit/pagecreate won't fire. Either learn the plugin, or start anew with jQM. The only tip I can give you is that we had a lot of issues running Chrome debugger when we dynamically loaded JS in the way I described. But if you load it all at the start adding breakpoints and debugging was much easier. See: http://stackoverflow.com/a/9085014/737023 - basically you want to listen for pageinit/pageshow for ALL pages and handle there – Clarence Liu Feb 11 '12 at 13:26
  • The only plugin I'm using is a switcher that I built myself; it detects user browser and switches between a desktop theme and a mobile theme - built in JQM. Unfortunately, I'm not very skilled at JS so it's difficult for me to see what's going on. But thanks for the tip on debugging and answering my initial question. – Stace Feb 11 '12 at 18:09
4

I encountered the same issue and couldn't fixed it with JQM event's model.

Finally I fixed it by adding rel="external" to all the anchors that are linking to a page with a google map. This solution desactivates pages transitions and preloaders.

Quick and dirty but... it works now.

  • Ultimately, this is what I had to do also. Wish I could have got it to work "properly" but it was just taking too much time. – Stace Apr 24 '12 at 19:07
3

I would suggest using the pageinit event instead:

From the jQuery docs:

http://jquerymobile.com/demos/1.0.1/docs/api/events.html

pageinit

Triggered on the page being initialized, after initialization occurs. We recommend binding to this event instead of DOM ready() because this will work regardless of whether the page is loaded directly or if the content is pulled into another page as part of the Ajax navigation system.

$( '#aboutPage' ).live( 'pageinit',function(event){
  alert( 'This page was just enhanced by jQuery Mobile!' );
});
andresf
  • 2,063
  • 1
  • 11
  • 7
2

I had similar issue, and I use external js file.

Several thing which I did wrong:

  • Need to use pageinit instead of jquery document ready
  • I had a class check to see if <body class="map" before I run my map loading, but it should be at the JQM's div page level like <div data-role="page" class="page-map">, since if the page is loaded via Ajax the body class will be irrelevant.
  • I need to get the correct map canvas, so at the end I set class="map_canvas" instead of id="map_canvas" to avoid id duplication.
  • I then need to know the current active page by doing activePage = $(event.target). With this I can get the correct canvas by $('.map_canvas', activePage). (I encountered once that the map is loaded into some hidden div)
  • Since I am using external js file, all the relevant js file need to be included in every JQM pages. I put them in the layout file which all JQM pages uses.

My code (coffeescript):

$(document).on "pageinit", (event) ->
  activePage = $(event.target)
  return if !$(activePage).is('.page-map')

  createMap = (options) ->
    canvas = $('.map_canvas', activePage)
    canvas.css(width: '100%', height: '90%')
    map = new google.maps.Map(canvas[0], options)

  address = $('.address', activePage).text()
  geocoder = new google.maps.Geocoder()

  geocoder.geocode {address: address}, (results, status) ->
    if status == google.maps.GeocoderStatus.OK
      options =
        zoom: 14
        center: results[0].geometry.location
        mapTypeId: google.maps.MapTypeId.ROADMAP

      map = createMap(options)
      map.setCenter(results[0].geometry.location)
lulalala
  • 17,572
  • 15
  • 110
  • 169
1

Amazing response of lulalala - thank you for solving my issue. I changed the HTML code (class="gmap_canvas" instead of id="gmap_canvas") according to his answer. In addition I added

google.maps.event.trigger(map, "resize");

to show the map properly.

See also function showMap for details.

In the html I changed the div tag for the map into:

<div data-role="content">
 <div id="container" class="container">
  <div class="gmap_canvas"></div>       
 </div>
</div><!-- div content -->

and I added a rel="external" - Attribute to all of the anchor tags:

<div data-role="navbar" data-theme="b">
<ul>
<li><a id="karteLink"   href="index.html" data-icon="karte" data-transition="fade" rel="external">KARTE</a></li>
<li><a id="listeLink"   href="liste.html" data-icon="liste" data-transition="fade" rel="external">LISTE</a></li>                    
<li><a id="optionsLink" href="favoriten.html"   data-icon="options"     data-transition="fade" rel="external">OPTIONEN</a></li>
</ul>
</div><!-- /navbar   --> 

My code (jquery)

    var activePage;

        $( '#karte' ).live( 'pageinit',function(event){
            activePage = $(event.target);
            initialize();
        });

        function initialize() {
            // prepare Geocoder   
            clearOverlays(); 
            var geocoder = new google.maps.Geocoder();
            navigator.geolocation.getCurrentPosition(function(position) {
                var myLatlng = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
                var myOptions = { // default map options
                    zoom: 12,
                    center: myLatlng,
                    mapTypeId: google.maps.MapTypeId.ROADMAP
                };
                showMap(myOptions);
                google.maps.event.trigger(map, "resize");
                findPlaces();
            });
        }


        function showMap( options ) {
            canvas = $('.gmap_canvas', activePage);
            canvas.css({width: '100%', height: '100%', position: 'relative'});
            map = new google.maps.Map(canvas[0], options);
            myLatLng = options.center;
        }

    // clear overlays function
    function clearOverlays() {
        if (markers) {
            for (i in markers) {
                markers[i].setMap(null);
            }
            markers = [];
            infos = [];
        }
    }

    // clear infos function
    function clearInfos() {
        if (infos) {
            for (i in infos) {
                if (infos[i].getMap()) {
                    infos[i].close();
                }
            }
        }
    }

    // find custom places function
    function findPlaces() {
        var cur_location = myLatLng;
        // prepare request to Places
        var request = {
            location: cur_location,
            radius: radius,
            type: [type],
            keyword: [keyword]
        };
        // send request
        service = new google.maps.places.PlacesService(map);
        service.search(request, createMarkers);
    }

// create markers (from 'findPlaces' function)
function createMarkers(results, status) {
    if (status == google.maps.places.PlacesServiceStatus.OK) {
        // if we have found something - clear map (overlays)
        //Eigene Winzer von der Datenbannk holen

        $.getJSON(urlInit, function(data){      
            $.each(data.winzer, function(restaurant, daten){
                var locationObject = new Object;
                locationObject.name = daten.name;
                locationObject.geometry = new Object;
                locationObject.geometry.location = new google.maps.LatLng(daten.latitude,daten.longitude);
                locationObject.vicinity = daten.ort;
                createMark(locationObject);
            }); 
        });

        // and create new markers by search result
        for (var i = 0; i < results.length; i++) {
            createMark(results[i]);
        }
    } else if (status == google.maps.places.PlacesServiceStatus.ZERO_RESULTS) {
        alert('Sorry, nothing is found');
    }
}

// creare single marker function
function createMark(obj) {

    // prepare new Marker object
    var mark = new google.maps.Marker({
        position: obj.geometry.location,
        map: map,
        title: obj.name,
        animation: google.maps.Animation.DROP
    });
    markers.push(mark);

    // prepare info window
    var infowindow = new google.maps.InfoWindow({
        content: '<img src="' + obj.icon + '" /><font style="color:#000;">' + obj.name + 
        '<br />Rating: ' + obj.rating + '<br />Vicinity: ' + obj.vicinity + '</font>'
    });


    // add event handler to current marker
    google.maps.event.addListener(mark, 'click', function() {
        clearInfos();
        infowindow.open(map,mark);
    });
    infos.push(infowindow);
}
asauerli
  • 386
  • 3
  • 3
0

Interesting, I need to use "pageshow" rather than "pageinit" when navigating from another page.

When using "pageinit", the on function is run, but the callback function to draw the map is not. When using "pageshow" as in $( document ).on( "pageshow", "#map-page", function() {, then the callback draw map is run just fine by jQuery.

So, while I would prefer to use "pageinit", that just doesn't work for me.

jedison
  • 908
  • 6
  • 15