0

I am adding a leafletjs map in a remarkjs slideshow. The map works fine if the slide containing the map div is visible on initial load of the web page. However, if the slide with the map div is not the visible slide then the map div is invisible, so the leaflet map doesn't work properly as all the tiles are not loaded in the div. What I'd like to do is load all the map divs in the slideshow, no matter which slide they may be on, and then have them show up properly when the slide containing the maps comes up.

Update: the suggested answer does seem like it should answer my question, but I am still stuck and not sure if I am on the right track. Below are more details.

Since I could be adding many maps to a slideshow, I am using a class name for the map divs.

// css
.map { width: 100vh; height: 100vh; }

// html
<div class="map" data-lat="47" data-lon="106"></div>
<div class="map" data-lat="0" data-lon="0"></div>
<!-- possibly on a different slide -->
<div class="map" data-lat="30" data-lon="28"></div>

// js
var maps = []; // for storing map elements
var mapDivs = document.getElementsByClassName('map');

for (var i=0, j=mapDivs.length; i<j; i++) {
    var map = L.map(mapDivs[i]).setView(
        [mapDivs[i].dataset.lat, mapDivs[i].dataset.lon], 
        13
    );

    // store map for later
    maps.push(map);
    L.tileLayer(…).addTo(map);
}

slideshow.on('showSlide', function (slide) {
    for (var i=0, j=maps.length; i<j; i++) {
        maps[i].invalidateSize();
    }
});

My logic says the above should work, but it doesn't. What am I doing wrong?

Update2: The map.invalidateSize() answer was indeed a part of the solution. My fault was that I was calling slideshow.on('showSlide', fn) but I should really have been calling slideshow.on('afterShowSlide', fn). The latter gives remarkjs an opportunity to actually create the current slide which may have the map div which in turn allows map.invalidateSize() to correctly fire and repaint the map.

punkish
  • 13,598
  • 26
  • 66
  • 101
  • 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 Sep 12 '17 at 07:19
  • seems like your suggested answer should solve my problem, however, as I explain with more detail, I am not successful. – punkish Sep 12 '17 at 08:27
  • First make sure your issue is indeed due to incorrect map size at initialization: does it suddenly work if you resize your browser window after the slide with map is shown? – ghybs Sep 12 '17 at 08:32
  • yes, the map does load correctly if I resize the browser. Which is why I believe that `map.invalidateSize()` should work. However, given my code above, it doesn't – punkish Sep 12 '17 at 08:34
  • Maybe the map container size is still not right when the `'showSlide'` event fires. What if you include a short delay, e.g. using `setTimeout`? – ghybs Sep 12 '17 at 08:43
  • you were on the right track. See my **Update2** for the solution. Many thanks for your help – punkish Sep 12 '17 at 08:45
  • Thank you for your feedback. You should turn your updates into your own answer and mark it as accepted, so that other users of `remarkjs` can see a solved issue. – ghybs Sep 12 '17 at 08:55
  • good suggestion… done – punkish Sep 12 '17 at 14:18

1 Answers1

0

leafletjs requires a div of width and height > 0px to exist, that is, be visible, not hidden, to download the required map tiles and show a map. Adding map divs to a remarkjs slideshow means one or more of those divs would be hidden when the slideshow is launched. Consequently, leafletjs would not be able to show a map in those divs (random tiles would be missing) until the browser is resized forcing leafletjs to recalc the size of the map divs and download the correct tiles. The solution is to fire the map.invalidateSize() method when a slide with a map div is displayed. Here is how I did it finally

/* css ******************************/
.map { width: 100vh; height: 100vh; }


<!---------------------------------------------
html
map divs possibly on different slides, with a
`map: true` property on each slide with a map
----------------------------------------------->

---
map: true
<div class="map" data-lat="47" data-lon="106"></div>
---
map: true
<div class="map" data-lat="0" data-lon="0"></div>
---
map: true
<div class="map" data-lat="30" data-lon="28"></div>

// js /////////////////////////////////////////
var maps = []; // for storing leaflet map elements
var mapDivs = document.getElementsByClassName('map');

for (var i=0, j=mapDivs.length; i<j; i++) {

    // store map for later
    maps.push(L.map(mapDivs[i]).setView(
        [mapDivs[i].dataset.lat, mapDivs[i].dataset.lon], 
        zoomLevel
    ));

    // add map to the tileLayer
    L.tileLayer(…).addTo(maps[i]);
}

// use the 'afterShowSlide' event to ensure the 
// map div has been created
slideshow.on('afterShowSlide', function (slide) {

    // only if the slide has a 'map' property
    if (slide.properties.map) {
        for (var i=0, j=maps.length; i<j; i++) {
            maps[i].invalidateSize();
        }
    }
});
punkish
  • 13,598
  • 26
  • 66
  • 101