2

I am making a web app for different areas using Leaflet library. I have different Bootstrap tabs, each tab opens a new map of a new area.

The problem I am facing is that the tiles on the first tab load but the tiles on the second tab do not. Where am I wrong?

This is how I am loading my maps for now...

/* MAP LAYERS */
var map = L.map('mapid',{
        zoomControl: true,
}).setView([24.2, 67.8], 9);

/* LOADING AND ADDING BASE MAPS */
var baseMap = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
                    attribution: '&copy; <a href="https://carto.com/attributions">CARTO</a> contributors',
}).addTo(map);

/* MAP LAYERS */
var map1 = L.map('map_id',{
        zoomControl: true,
}).setView([22.2, 63.8], 9);

/* LOADING AND ADDING BASE MAPS */
var baseMap = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
                    attribution: '&copy; <a href="https://carto.com/attributions">CARTO</a> contributors',
}).addTo(map1);

My tabs:

<div  id = "locations">
                <ul class="nav nav-pills nav-fill" role="tablist">
                    <li class="nav-item">
                        <a class="nav-link active" href="#tabA" data-toggle="pill" role="tab">I</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="#tabB" data-toggle="pill">J</a>
                    </li> 

                </ul> 
            </div> <!-- End Tabs -->

The tab linkage:

<div class="tab-pane fade show active" id="tabA" role="tabpanel"> <!-- First Tab -->
<div class="col-6" id = 'map_grid'> <!-- Maps -->
                                <div id ="mapid"> </div> <!-- Div for the Maps -->
                            </div> 
   </div>


<div class="tab-pane fade" id="tabB" role="tabpanel"> <!-- Second Tab -->
<div class="col-6" id = 'map_grid'> <!-- Maps -->
                                <div id ="map_id"> </div> <!-- Div for the Maps -->
                            </div> <!-- End Maps -->
</div>
  • Map loads in DOM <div> element with id that is specified in the map initialization call. You have only <div> with id='map_id' for map1, but no <div> with id='mapid' for map. – TomazicM Feb 1 at 15:36
  • I do. I just didn't post those lines... let me edit it... – Aneeqa Feb 1 at 15:54
2

When Leaflet map is displayed in it's <div> element, map's size is calculated on the basis of the <div> element size at the moment the map is added. If element is hidden, as it is the case of Boostrap tab above, <div> element has no size and consequently map has no size when tab is displayed.

Leaflet map has invalidateSize() method just for such cases. When this method is called, map's size is adjusted to the current size of it's <div> element.

All that is needed then is to catch the moment (event), when tab (HTML element) is displayed. One of the possible ways is to use MutationObserver DOM object to catch style changes in HTML element that holds the map.

Below is quick simple example of that can be done.

CSS

<style>
  html, body {
    height: 100%;
  }
  .container-size {
    height: 100%;
  }
  .tab-size {
    height: calc(100% - 40px);
  }
  .map-size {
    height: 100%;
  }
</style>

HTML

<div class="container container-size">
  <ul class="nav nav-pills">
    <li class="active"><a data-toggle="pill" href="#home">Home</a></li>
    <li><a data-toggle="pill" href="#menu1">Menu 1</a></li>
  </ul>  
  <div class="tab-content tab-size">
    <div id="home" class="tab-pane fade in active map-size">
    </div>
    <div id="menu1" class="tab-pane fade map-size">
    </div>
  </div>
</div>

JS

<script>
  var map1 = L.map('home').setView([60, 30], 9);

  L.tileLayer('http://tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: 'Map &copy; OpenStreetMap'
  }).addTo(map1);

  var map2 = L.map('menu1').setView([60, 30], 9);

  L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
    attribution: '&copy; <a href="https://carto.com/attributions">CARTO</a> contributors',
  }).addTo(map2);

  var homeTab = document.getElementById('home');
  var observer1 = new MutationObserver(function(){
    if(homeTab.style.display != 'none'){
      map1.invalidateSize();
    }
  });
  observer1.observe(homeTab, {attributes: true});  

  var menu1Tab = document.getElementById('home');
  var observer2 = new MutationObserver(function(){
    if(menu1Tab.style.display != 'none'){
      map2.invalidateSize();
    }
  });
  observer2.observe(menu1Tab, {attributes: true});  
</script>
  • Thank you so much. I wasn't aware of the MutationObserver. That sorted my problem. – Aneeqa Feb 6 at 10:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.