Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Map API BackBoneJS Cannot read property 'offsetWidth' of null

I've been through as many StackOverflow/google groups as I can imagine trying to figure this guy out.

I'm using BackboneJS to render a map that has a start location and an end location. On a fresh page/page refresh, I don't get this error, and the map and stuff work fine, for I am using jQuery's $(window).load(.....) function; however, when I dynamically render my View, I get this error-I believe-because the DOM hasn't loaded the DIV yet (failing with document.getElementById). I have tried all manner of different methods other than the $(window).load(), but I can't get anything which works for both use cases (fresh page load -- BackboneJS view loading). Trying to call the function right after the template doesn't work, either.

Any help would be appreciated.

Robert

View:

    App.Views.MapShow = Backbone.View.extend({
      initialize: function() {
        _.bindAll(this, 'render');
        var self = this;
        $(window).load(function() {
          self.renderMap();
        });
      },

      render: function() {
        this.renderTemplate();
      },

      renderTemplate: function() {
        this.$el.html(JST['path/to/show/file']());
      },

      renderMap: function() {
        var from     = this.model.get('location_from');
        var to       = this.model.get('location_to');
        var geocoder = new google.maps.Geocoder();
        var map      = new google.maps.Map(document.getElementById('mapCanvas'), {
          mapTypeId: google.maps.MapTypeId.ROADMAP
        });
        var directionsService = new google.maps.DirectionsService();
        var directionsDisplay = new google.maps.DirectionsRenderer();

        directionsDisplay.setMap(map);

        var request = {
          origin: from,
          destination: to,
          travelMode: google.maps.DirectionsTravelMode.DRIVING
        };

        directionsService.route(request, function(response, status) {
          if (status == google.maps.DirectionsStatus.OK) {
            directionsDisplay.setDirections(response);
          }
        });
      }
    });

HTML:

    <div class="map" id="mapCanvas"></div>
like image 607
rpearce Avatar asked Aug 12 '12 20:08

rpearce


1 Answers

I'd guess that your problem is that #mapCanvas isn't in the DOM until after you try to access it so this:

document.getElementById('mapCanvas')

will give you a useless null. You need to wait until #mapCanvas is in the DOM before using it; you can't do something like this either:

map_canvas = this.$el.find('#mapCanvas')[0];

That will give you a valid ID but you'll confuse the Google Maps functions because it won't have a size so the map will be rendered oddly. This puts you back to waiting for everything to be in the DOM before binding your Google Maps stuff.

One way around this is to use setTimeout with a delay of zero:

var _this = this;
setTimeout(function() { _this.renderMap() }, 0);

This looks strange bit it does work, this trick basically dumps your renderMap call into the browser's work queue and it'll get around to running it once you've returned control to the browser.

You can also use _.defer:

defer _.defer(function, [*arguments])

Defers invoking the function until the current call stack has cleared, similar to using setTimeout with a delay of 0. Useful for performing expensive computations or HTML rendering in chunks without blocking the UI thread from updating.

This might be a better choice as it makes your intent explicit.

like image 131
mu is too short Avatar answered Oct 12 '22 09:10

mu is too short