Question

Thank you in advance for any help, I have been pulling my hair out for a couple days now. I am trying to create a jQuery Google map plugin which, beyond many other things will allow for a user to specify a path to a custom icon and icon shadow. This should then, be able to figure out the dimensions of the images and place them on the map. Everything is working great except for that in IE9 and Chrome on certain page refreshes, only one icon shows and I get an uncaught error:INVALID_STATE_ERR: DOM Exception 11. FYI, it seems to work all the time in Firefox.

Here is the plugin (stripped down for example):

;
(function ($, undefined) {
$.fn.irvMap = function (options) {
    var directionsDisplay, directionsService = new google.maps.DirectionsService(),
        map, cloud, counter = 0,
        i, markers = [],
        infowindow = null,
        request = null,
        settings = $.extend({
            locations: [{
                "name": "Midland",
                "lat": "43.620916",
                "lng": "-84.248028"
            }, {
                "name": "Texas",
                "lat": "32.737328",
                "lng": "-97.107525"
            }]
        }, options);
    var lat = 0;
    var lng = 0;
    var halfzies = function (i) {
            return i / 2
        }
    return this.each(function () {
        var thisElement = this;
        var mapIcon = new Image();
        var mapIconShadow = new Image();
        var icons = mapIcon,mapIconShadow;
        mapIcon.src = "/Images/mapIcon.png";
        mapIconShadow.src = "/Images/mapIconShadow.png";
        icons.onload = function () {
            var image = new google.maps.MarkerImage(mapIcon.src, new google.maps.Size(mapIcon.width, mapIcon.height), new google.maps.Point(0, 0), new google.maps.Point(halfzies(mapIcon.width), halfzies(mapIcon.height)));
            var shadow = new google.maps.MarkerImage(mapIconShadow.src, new google.maps.Size(mapIconShadow.width, mapIconShadow.height), new google.maps.Point(0, 0), new google.maps.Point(halfzies(mapIconShadow.width), halfzies(mapIconShadow.height)));
            var makeMarker = function (location, id) {
                    var point = new google.maps.LatLng(location.lat, location.lng);
                    var markerOptions = {
                        map: map,
                        shadow: shadow,
                        icon: image,
                        position: point
                    };
                    var marker = new google.maps.Marker(markerOptions);
                    //markers.push(marker);
                    marker.setMap(map);
                    var content = '<input type=\"hidden\" id=\"end\" value=\"' + location.lat + ',' + location.lng + '\"/>';
                    content = content.replace(/undefined/g, '');
                    google.maps.event.addListener(marker, 'click', function (e) {
                        if (infowindow) {
                            infowindow.close();
                        }
                        infowindow = new google.maps.InfoWindow({
                            position: marker.getPosition(),
                            map: map,
                            content: content
                        });
                        if (request != null) {
                            request.destination = marker.getPosition()
                            getDirections(request);
                            $('html,body').animate({
                                scrollTop: offset
                            }, 500);
                        }
                    });
                }
            $.each(settings.locations, function (i, val) {
                lat += parseFloat(this.lat) || 0
                lng += parseFloat(this.lng) || 0
            });
            lat = lat / settings.locations.length;
            lng = lng / settings.locations.length;
            var options = {
                center: new google.maps.LatLng(lat, lng),
                mapTypeId: google.maps.MapTypeId.ROADMAP
            }
            map = new google.maps.Map(thisElement, options);
            var bounds = new google.maps.LatLngBounds();
            $.each(settings.locations, function (i, val) {
                makeMarker(settings.locations[i], i);
                var ll = new google.maps.LatLng(settings.locations[i].lat, settings.locations[i].lng);
                bounds.extend(ll);
            });
            map.fitBounds(bounds);
        }
    })
};
})(jQuery);

I am pretty sure it has to do something with this:

  var mapIcon = new Image();
  var mapIconShadow = new Image();
  var icons = mapIcon,mapIconShadow;
  mapIcon.src = "/Images/mapIcon.png";
  mapIconShadow.src = "/Images/mapIconShadow.png";
  icons.onload = function () {

But I have played with this code for the last 3 days and cannot seem to figure it out. Here is my test page and again any help is much appreciated: http://lab.interactrv.com/preloadTest.aspx

Thanks.

Was it helpful?

Solution

The problem appears to stem from this line:

var icons = mapIcon,mapIconShadow;

The way I see it, icons only gets assigned to the icon, not shadow, for example, running

var x = 1;
var y = 2;
var n = x, y;

alert(n);  // gives 1, but 2 is in no way associated to n

So icons.onload is really only checking if the icon is loaded, not the shadow. If the shadow loads first, all is good. If the icon happens to load before the shadow, you will get undefined properties.

You will have to find a way to trigger the bulk of the function only when both icon and shadow are loaded.

I tried jQuery selectors like $("img").load(), and even assigning IDs to the newly created icon and shadow, to capture the preloading of both icon and shadow, but was unsuccessful. Ultimately I put together this pair of boolean conditions and while I can't prove it is bulletproof, it survived ten Chrome reloads.

I leave it to you to write up more sophisticated code :)

    return this.each(function () {
        var thisElement = this;
        var mapIcon = new Image();
        var mapIconShadow = new Image();
        //var icons = mapIcon,mapIconShadow;

        mapIcon.src = "http://lab.interactrv.com/Images/mapIcon.png";
        mapIconShadow.src = "http://lab.interactrv.com/Images/mapIconShadow.png";

    var iconLoaded = false;
    var iconShadowLoaded = false;

        mapIcon.onload = function() { iconLoaded = true; if(iconShadowLoaded) { makeMarkerImages(); } };
        mapIconShadow.onload = function() { iconShadowLoaded = true; if(iconLoaded) { makeMarkerImages(); } };

        var makeMarkerImages = function () {

            var image = new google.maps.MarkerImage(mapIcon.src, new google.maps.Size(mapIcon.width, mapIcon.height), new google.maps.Point(0, 0), new google.maps.Point(halfzies(mapIcon.width), halfzies(mapIcon.height)));
        // CONTINUED AS BEFORE
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top