24

i have a big problem. i want to open a leaflet map in a modal. but the map is not showing properly. the tiles are not loading.

here is the script:

https://codeply.com/p/TYUqgVvYAQ

<a href="#myModal" role="button" class="btn btn-primary" data-toggle="modal">Open Map</a>

<div id="myModal" class="modal">
<div class="modal-dialog">
  <div class="modal-content">
    <div class="modal-header">
      <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
      <h4 class="modal-title">Map</h4>
    </div>
    <div class="modal-body">
      <div class="modal-body" id="map-canvas"></div>
    </div>
    <div class="modal-footer">
      <button type="button" class="btn" data-dismiss="modal" aria-hidden="true">OK</button>
    </div>
  </div>
</div>
$.getScript('http://cdn.leafletjs.com/leaflet-0.7/leaflet.js',function(){

 /* map settings */
 var map = new L.Map('map-canvas');
 var cloudmade = new    L.TileLayer('http://{s}.tile.cloudmade.com/f1376bb0c116495e8cb9121360802fb0/997/256/{z}/{x} /{y}.png', {
 attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a>   contributors, Imagery © <a href="http://cloudmade.com">CloudMade</a>',
 maxZoom: 18
 });
 map.addLayer(cloudmade).setView(new L.LatLng(41.52, -71.09), 13);


 });

any help much apreciated

Carol Skelly
  • 351,302
  • 90
  • 710
  • 624
user2975100
  • 267
  • 1
  • 4
  • 9

10 Answers10

59

I think what is happening is that, when the map is created, the container width/height for your `map-canvas' element has not yet been adjusted to the width/height of the modal dialog. This causes the map size to be incorrect (smaller) than what it should be.

You can fix this by calling map.invalidateSize(). This will work to re-adjust the width/height bounds of the L.Map's container.

You can automatically call this by hooking into the event where the Bootstrap modal becomes shown.

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

Insert this code into your JavaScript. When the modal is shown, the map will then invalidate its size. The timeout is because there may be some animation/transition time for the modal to display and be added to the DOM.

Patrick D
  • 6,659
  • 3
  • 43
  • 55
  • maybe you can help me out with this also: http://stackoverflow.com/questions/20404174/open-modal-with-marker-leaflet-bootstrap-3 :) – user2975100 Dec 05 '13 at 15:39
  • 3
    In some cases 10 milliseconds could be a very small time considering the time that takes the modal to be fully loaded. Its a good idea increase the time before invalidateSize() is called and/or put a refresh button in the modal to call that function when it gets clicked. – francordie Mar 19 '14 at 02:18
  • A 'refresh' button would certainly provide a gaurentee. – Patrick D Mar 20 '14 at 16:45
  • 1
    My issue wasn't modal related but still missing. I had to add a dimension to style. I choose to go with height. `
    `
    – Devin Gleason Lambert Jul 15 '16 at 17:12
24

You should probably avoid using setTimeout with a randomly chosen delay. A better way using the 'shown.bs.modal' event instead of 'show.bs.modal':

modal.on('shown.bs.modal', function(){
    setTimeout(function() {
        map.invalidateSize();
   }, 1);
})

Or use underscore's defer :

modal.on('shown.bs.modal', function(){
    _.defer(map.invalidateSize.bind(map));
})
Erik
  • 489
  • 4
  • 8
  • 9
    ^ This should be the accepted answer. Additionally because the 'shown' event is fired once the modal is visible you no longer need to queue the invalidateSize() method do you? You can just use modal.on('shown.bs.modal', function () { map.invalidateSize(); }); (source: http://getbootstrap.com/javascript/#modals-events) – linuscash Sep 10 '15 at 13:55
  • Yeah, this is better. – Patrick D Jun 10 '16 at 14:25
8

I use this workaround:

.modal {
  visibility: hidden;
  display: block;
}

.modal[aria-hidden='false'] {
  visibility: visible;
  display: block;
}
2

I tried map.invalidateSize() but not fixed.

finaly it fixed by this:

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

In the function, put the codes related to loading the map.

AliReza Abbasian
  • 51
  • 1
  • 1
  • 7
  • This answer to an 8 year old question is not helpful: it speaks of a specific solution for someone else. – gdvalderrama Nov 11 '22 at 17:25
  • @gdvalderrama But after a few hours of searching, I found this answer that was my solution and I'm sure it will be the solution of many others. – AliReza Abbasian Nov 13 '22 at 08:04
1

This works for me, you can read here

map.whenReady(() => {
    console.log('Map ready');
    setTimeout(() => {
        map.invalidateSize();
    }, 0);
});
Alex Montoya
  • 4,697
  • 1
  • 30
  • 31
0

This worked for me -

 $('#gmap').on('shown.bs.tab', function (e) {
    //call the clear map event first
    clearMap();
    //resize the map - this is the important part for you
   map.invalidateSize(true);
   //load the map once all layers cleared
   loadMap();
})
Tajuddin
  • 73
  • 3
  • 13
0

leaflet map uncorrect display on load can be solved with this :

  var mapid = $(this).find('[id^=leaflet-map]').attr('id');
  var map = settings.leaflet[mapid].lMap;
  map.invalidateSize();
Matoeil
  • 6,851
  • 11
  • 54
  • 77
0

How I solved this issue: init map in main window. Then move map to opened modal.

0

First, create the map in div outside of the Moodle

<div>
  <div class="modal-body" id="map-canvas" style="width: 100%;height: 300px;"></div>
</div>

And write same js code :

 /* map settings */
 var map = new L.Map('map-canvas');
 var cloudmade = new    L.TileLayer('http://{s}.tile.cloudmade.com/f1376bb0c116495e8cb9121360802fb0/997/256/{z}/{x} /{y}.png', {
 attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a>   contributors, Imagery © <a href="http://cloudmade.com">CloudMade</a>',
 maxZoom: 18
 });
 map.addLayer(cloudmade).setView(new L.LatLng(41.52, -71.09), 13);


 });

Then move the map to the Moodle :

$('#map-canvas').appendTo('.modal-body');
-1

for vue.js and nuxt.js developers , probably it's because of using v-show or v-if but you shouldn't use v-if and instead use v-show. after that the only thing u need is using client-only tag like this:

<client-only>
<div v-show="someVariable" id="vShowOrVIfExample">
<div id="map-wrap" style="height: 100vh">
   <l-map :zoom=13 :center="[55.9464418,8.1277591]">
     <l-tile-layer url="http://{s}.tile.osm.org/{z}/{x}/{y}.png"></l-tile-layer>
     <l-marker :lat-lng="[55.9464418,8.1277591]"></l-marker>
   </l-map>
 </div>
</div>
</client-only>
Arian Fm
  • 314
  • 4
  • 14