Google Maps JS v3: Map display: none; after map initialization causing corrupted map

13,447

Solution 1

google.maps.event.addListenerOnce(map, 'idle', function() {
    $('#addstop').css({
        display: 'none',
        left: '0',
        top: '0'
    });
});

This event happens only once after the map is fully loaded and 'idle'

Solution 2

Could you try calling

//map hold's a reference to your current map
google.maps.event.trigger(map, 'resize');

After the map/div containing it becomes visible?

Solution 3

Yup -- I had this same problem.

What I did was trigger the initialization after the event button that displays the hidden map is clicked.

So I have a hidden div, when it's clicked to shown, i display it and then initalize it. Is this doable for what you're trying to achieve? I'm assuming you want performance in that you'd prefer the click to instantly show a populated map -- however it isn't too slow to populate the small area you're tying to if you do it on the click event.

Share:
13,447
BentFX
Author by

BentFX

(my about me is currently blank)

Updated on June 09, 2022

Comments

  • BentFX
    BentFX almost 2 years

    This certainly touches on previous questions regarding map display during initialization. Yet the issue here is with map display being set to none after map should have already initialized. The last line of my widow.onload sets the map to display: none; The map initialization should have already completed by that time, but the fact remains, the final call is causing the problem.

    window.onload(); function...

    window.onload = function(){
      changeTheme(me); // do it now so current_theme is avaible to switchTabs();
        switchTabs("tab3"); // sets map div visible
      initMaps(); // map initialization. code included.
      loadFavoritePlaces(); // asynch $getJSON call, adds markers. No matter the condition of map, markers appear in their proper locations.
      closePopup("images");
      closePopup("location"); // sets maps.mini_map display: none; Problems if we loadUserTable() later. Otherwise OK. Odd!
      closePopup("tweet");
      centerDiv();
      document.title = '@'+me.screen_name+' - PithyTwits.com';
      users[me.id_str] = me;
      getPage(); // asynch $.getJSON loads tweets. Not an issue.
      
      // Append a scroll event handler to tweet_div
      $("#tweet_div").scroll(function() {
        var pos = $(this)[0].scrollHeight - $(this).scrollTop();
        if(pos != prev_scroll){ // hack to prevent scroll function from firing twice
          prev_scroll = pos;
          if (pos == $(this).outerHeight()) {
            $("#throbber").fadeIn();
            getPage();
          }
        }
      });
      
      loadUserTable(me.id_str);
      /* loadUserTable(); calls switchTabs("tab1"); which sets map div display: none;
      if I comment this out the map initialization completes properly, but my 'tab1'
      doesn't get populated properly. And page doesn't start on 'tab1', which is required. */
      
    // end window.onload()
    }
    

    initMaps(); function...

    function initMaps() {
        
        // markers list
      maps.markers = new Object;
      
      // visibility status'
      maps.markerStatus = new Object;
      maps.markerStatus['query'] = true;
      maps.markerStatus['tweet'] = true;
      maps.markerStatus['favorite'] = true;
      
      // define marker images
      maps.reticleImage = new google.maps.MarkerImage('images/reticle.png',
        new google.maps.Size(63, 63),
        new google.maps.Point(0,0),
        ...
        Declarations removed to streamline post.
        ...
        new google.maps.Point(0,0),
        new google.maps.Point(1, 13));
      maps.markerShape = {
          type: "poly",
          coords: [9,22,16,11,16,5,11,1,6,1,2,5,2,11,9,22]
          }
        
      // setup map options
      var latlng = new google.maps.LatLng(39.520427, -94.770621);
      var latlng2 = new google.maps.LatLng(46.1912, -122.1944);
      var myOptions = {
        zoom: 3,
        center: latlng,
        mapTypeId: google.maps.MapTypeId.HYBRID
      };
      var myOptions2 = {
        zoom: 13,
        center: latlng2,
        disableDefaultUI: true,
        draggable: false,
        keyboardShortcuts: false,
        mapTypeControl: false,
        scrollwheel: false,
        mapTypeId: google.maps.MapTypeId.HYBRID
      };
      
      // initialize maps
      maps.main_map = new google.maps.Map(document.getElementById("map_div"), myOptions);
      maps.mini_map = new google.maps.Map(document.getElementById("mini_map"), myOptions2);
    
      // default map center markers
        maps.mini_map_marker = new google.maps.Marker({
        position: latlng2,
        map: maps.mini_map,
        icon: maps.favoriteMarker, 
        shadow: maps.markerShadow,
      });
      maps.reticleMarker = new google.maps.Marker({
        position: latlng,
        map: maps.main_map,
        shape: reticleShape,
        icon: maps.reticleImage,
      });
      
      // event handlers
      google.maps.event.addListener(maps.main_map, 'zoom_changed', mapZoomed);
      google.maps.event.addListener(maps.main_map, 'bounds_changed',
          function(){maps.reticleMarker.setPosition(maps.main_map.getCenter());});
    
      //idle event listener provided by @Guan in the marked answer.
      google.maps.event.addListenerOnce(maps.main_map, 'idle', function() {
          var div = document.getElementById("tab3_content"); 
          div.style.display = "none"; 
          div.style.position = "relative"; 
          div.style.left = "0px"; 
      });
    
      // initialize controls
      var controls = document.getElementById("visibility_controls");
      maps.main_map.controls[google.maps.ControlPosition.TOP_CENTER].push(controls);
      controls.style.display = "inline";
      var controls = document.getElementById("control_controls");
      maps.main_map.controls[google.maps.ControlPosition.RIGHT_CENTER].push(controls);
      controls.style.display = "inline";
      var controls = document.getElementById("query_controls");
      maps.main_map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(controls);
      controls.style.display = "inline";
    }
    

    If I call loadUserTable(); at the end of window.onload(); I get this... (munged)

    Maps not properly initialized

    If I don't call loadUserTable(); at the end of window.onload(); I get this... (correct)

    Maps properly initialized

    Since the problem stems from the maps display being set to none after the maps should have initialized, it would lead one to believe that the map initialization is actually happening non-syncronously. So how do I know when it is finished, and it is safe to hide the maps div? And also there is the question of why the mini_map seems to be dependent on visibility of the main_map, rather than its own visibility? I get the same results in both Chrome and Firefox, on Linux.

    Any help is help :)

    Skip

    UPDATE: I changed the final call to setTimeout("loadUserTable();", 1000); and 1 second is enough of a pause to let things work, but isn't what I want! Since @Jobsz verifies this is known issue, I'm going to resort to off screen initialization, and move the map into position either when needed for display, or hide it and put it in position after a short timeout.

    SOLUTION: Provided by @Guan (Checked answer)

    I did not want the map visible during initialization. But wanted it initialized and ready when the user chose that tab.

    The map div is initially set thus...

    id="tab3_content" style="display: block;position: absolute; left: -1000px;"
    

    That makes it visible, but offscreen to the left.

    And then set a listener for the idle event in the map initialization...

    google.maps.event.addListenerOnce(maps.main_map, 'idle', function() {
      var div = document.getElementById("tab3_content"); 
      div.style.display = "none"; 
      div.style.position = "relative"; 
      div.style.left = "0px"; 
    });
    

    That event fires once when the map is idle(ready). It hides the div and moves it into position on screen.

    The loadUserTable() function is called in the normal program flow, and life is good. :)

  • BentFX
    BentFX almost 13 years
    Thanks @Jobsz for the quick reply. Sometimes the correct answer is "Been there, done that, fight with it!" The setTimeout(); proves to me it is just a question of allowing time for it to work. It's gonna have to work offscreen! Still there is the confusuion of why the main_map display corrupts the mini_map? Odd! Unless someone can expose an initialization complete callback, you've got the final answer.
  • Atticus
    Atticus almost 13 years
    Yeah man I'd really like to know the issue with the hidden divs as well.. I did a fair amount of searching on this topic myself with no luck.. You could definitely do a setTimeout and have a loader icon during the duration for looks. but yeah, my suggestion is to initialize on the toggle. I mapped out into a data set if the map is initialized already to prevent redoing the job. Might be something you want to do too if you're dealing with multiple preview maps, which i think you are.
  • BentFX
    BentFX almost 13 years
    Awesome function. Definitely worth a vote up. I had to roll back my code to recreate the issue, just to watch 'resize' work. It straightens it all out. For now I'm initializing off screen, but am sticking this into a comment in my initialization just in case I need it later. Thanks!
  • BentFX
    BentFX almost 13 years
    Preview maps??? No, the mini_map just shows zoom in, and details for selected markers, and when you hover on the location icon in a tweet. My next hurdle is to implement a similar function in an infoWindow for selected markers, and leave the mini_map dialog for locations in tweets.
  • Atticus
    Atticus almost 13 years
    Yeah by preview i meant the selected region in a zoom box like you're doing. Same stuff, anyhow -- you just want to populate your info window with external data right?
  • BentFX
    BentFX almost 13 years
    Yeah, it's all external (twitter) data. Every location has an id, and I'm collecting id's from tweets. Saving favorites. And querying for nearby places, or city wide searches. It all seems to be coming together. Thanks for your input!
  • Atticus
    Atticus almost 13 years
    Okay, well in any case this is what I do to put some dynamic data into my infowindow: pastie.org/1879448
  • BentFX
    BentFX almost 13 years
    Oh, "multiple preview maps", I get it now... Sometimes I'm dense! No, it's just the one mini_map... I just reset its center, move the marker to the new center, and change the marker icon to reflect the type of location we're zoomed in on. As far as I can see, if it gets initialized properly, it's good to go.
  • BentFX
    BentFX almost 13 years
    The downside I see to using the resize event as a part of the initial display... If the map is initialized hidden, it seems to be treated as a 0x0 map with the center in top left corner. Then when you trigger the resize event it realizes the new bounds, without moving the top left corner, and you lose your initial map center.
  • BentFX
    BentFX over 12 years
    That looks like a powerful thing! It's been 8 months. I've got to go dig back into my code. Up vote for now... if it does what it looks like I'll move the check. Off screen has its advantage, but the gist of the question was "How do I know when the map is ready." This answers that. Didn't know about the 'idle' event. Should be able to also set a plain listener for idle then unset it on the first hit??? Anyhow, Thanks!
  • BentFX
    BentFX over 12 years
    That does it. Thank you. Your first StackOverflow answer is a winner. Keep it up :) I still initialize off screen but this fires and moves the div when it is ready.
  • hitautodestruct
    hitautodestruct over 11 years
    @BentFX Also consider the tilesloaded event if you want to know when everything has come to a rest. Reference
  • José
    José almost 9 years
    This is the best approach. I use and recommend. Sending something away with CSS's position and left isn't good. There will always be displays that will show the stuff. It's like putting all the dirty under the carpet. Loading only if required prevents bandwidth being used uselessly. Two birds, one stone. P.S.: This problem doesn't happen while at Safari on my iPad Mini with Retina Display (iOS 8.x). I guess that's because it has something that prevents things being loaded in 'second plane'. So with display: none to display: block, makes Safari resume the map loading.