4

Having a problem where Mapbox using Leaflet is not displaying correctly in a modal.

The map and marker are in the upper left and the rest of the tiles are blank.

I have tried the code directly in a page and not a modal and it works...

<style>
    #mapid { height: 300px; }
</style>
<div id="mapid"></div>
<script>
    var mymap = L.map('mapid').setView([<?= $return['latitude']; ?>, <?= $return['longitude']; ?>], 13);
    //var mymap  =  L.Map('mapid', { center: new L.LatLng(<?= $return['latitude']; ?>, <?= $return['longitude']; ?>]), zoom: 15, layers: [nexrad], zoomControl: true });
    L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=<?= $MapBoxToken ?>', {
        attribution: '&copy; <a href="http://mapbox.com">Mapbox</a>, &copy; <a href="http://openstreetmap.org">OpenStreetMap</a>',
        maxZoom: 20,
        id: 'mapbox.streets',
        accessToken: '<?= $MapBoxToken ?>'
    }).addTo(mymap);    
    var marker = L.marker([<?= $return['latitude']; ?>, <?= $return['longitude']; ?>]).addTo(mymap);
</script>

screen shot of modal and map

Screen shot of modal and map with display style removed on map to show actual map.

ghybs
  • 47,565
  • 6
  • 74
  • 99
tpage
  • 123
  • 2
  • 8
  • It seems to only be a desktop browser wifi problem. Seems to be ok on mobile and LTE but not wifi... – tpage Mar 15 '18 at 19:54
  • Possible duplicate of [Data-toggle tab does not download Leaflet map](https://stackoverflow.com/questions/36246815/data-toggle-tab-does-not-download-leaflet-map) – ghybs Mar 16 '18 at 03:47
  • It seems to correct itself if you resize the browser window. Wondering if it is possible to resize the browser window, not the modal window, automataly by a -1px then +1px after the map loads. The map is in a modal window... – tpage Mar 22 '18 at 16:46

2 Answers2

10

As explained in the linked post, having the map work when the browser window is resized is just a symptom of the issue. Leaflet listens to browser window resize event, and reads again the map viewport dimensions when it occurs, so that it can properly display tiles and center the view.

Under the hood, Leaflet simply calls map.invalidateSize() on the resize event.

So the proper solution for your case is NOT to have the browser window be resized, but to call map.invalidateSize().

And do that NOT after your map loads, but after your modal is opened, so that the map viewport now has its final dimensions.

Since you added the Bootstrap tag to your question, I guess your modal is built using Bootstrap. In that case, you can listen to the "shown.bs.modal" event:

var map = L.map('map').setView([48.86, 2.35], 11);

L.marker([48.86, 2.35]).addTo(map);

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);

// Comment out the below code to see the difference.
$('#myModal').on('shown.bs.modal', function() {
  map.invalidateSize();
});
<!-- Bootstrap assets -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>

<!-- Leaflet assets -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet-src.js" integrity="sha512-IkGU/uDhB9u9F8k+2OsA6XXoowIhOuQL1NTgNZHY1nkURnqEGlDZq3GsfmdJdKFe1k1zOc6YU2K7qY+hF9AodA==" crossorigin=""></script>

<!-- Button trigger modal -->
<button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">
  Launch demo modal that contains the map container.
</button>

<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        <h4 class="modal-title" id="myModalLabel">Modal title</h4>
      </div>
      <div class="modal-body">

        <!-- map container -->
        <div id="map" style="height: 180px"></div>

      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-primary" data-dismiss="modal">Close</button>
      </div>
    </div>
  </div>
</div>
ghybs
  • 47,565
  • 6
  • 74
  • 99
  • ghybs - Think the "shown.bs.modal" worked! Will run a few test on different devices to make sure. THANKS! – tpage Mar 23 '18 at 15:43
1

The problem seem to with bootstap modal.

Needed to add map.invalidateSize(); but within "shown.bs.modal", as per ghybs suggestion.

$('#myModal').on('shown.bs.modal', function() {
  map.invalidateSize();
});

Note that the "map" in map.invalidateSize(); needs to be replace with the varable assigned in "var map = L.map()". In my original code in this post it was:

var mymap = L.map('mapid').setView([<?= $return['latitude']; ?>, <?= $return['longitude']; ?>], 13);

so I added:

$('#modalID').on('shown.bs.modal', function() {
  mymap.invalidateSize();
});
tpage
  • 123
  • 2
  • 8