86

I have a <div> containing a leaflet map. Upon certain events the height of the <div> will be altered. I'd like for the map to resize to the new dimensions of its surrounding <div> so that the old center is centered in the resized smaller or larger map. I tried using the invalidateSize() function, but it doesn't seem to work at all. How can I resize and center the map after that map-container-resize event?

$mapContainer.on('map-container-resize', function () {
   map.invalidateSize(); // doesn't seem to do anything
});

Edit to give more context:

The map container is styled initially as

#map-container {
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;

    transition: height 0.5s ease-in-out;
}

After a user clicks a certain button, another panel shows at the bottom of the page and the map-container's height will be reduced to something less than 100% (say 80%).

Upon click on this button, the map-container-resize event is triggered so that I can make the map resize and center on its old (i.e. before the resizing happened) center. The map itself should then also be resized to 80% of its initial height.

The APi doc for invalidateSize seemed to be what I wanted:

"Checks if the map container size changed and updates the map if so [...]"

But having a look with the output of the getSize function before and after the call to invalidateSize, nothing is different, the map remains at its old size.

3
  • 1
    This needs more context. Are you actually firing a map-container-resize event? How is the map sized in CSS?
    – tmcw
    Commented Jun 25, 2014 at 22:13
  • 1
    did you mean 'resize' event ? this one works
    – YaFred
    Commented Jun 26, 2014 at 9:50
  • on('resize') only applies to WINDOW resizing, and map-container-resize does not exist. so what was the actual event here?
    – phil294
    Commented Aug 1, 2016 at 13:49

8 Answers 8

113

The problem is that the resizing of the #map-container div is done via a css transition. The transition hasn't started yet, let alone ended, when the call to invalidateSize happens so the leaflet map cannot recognize any change of dimensions of its surrounding div.

Triggering the map-container-resize event with a delay solved the problem. This way :

setTimeout(function(){ map.invalidateSize()}, 400);
2
29

I came across this question today and wanted to provide an updated answer based on 2020 Browser API. This example uses the Browser's ResizeObserver to monitor the size of the div that Leaflet is mounted too. Assuming the following HTML Snippet:

<div id="map" />

With the following JavaScript:

const mapDiv = document.getElementById("map");
const map = L.map(mapDiv).setView([51.505, -0.09], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);

const resizeObserver = new ResizeObserver(() => {
  map.invalidateSize();
});

resizeObserver.observe(mapDiv);

This should monitor the map div, and call the invalidateSize() method on the Leaflet map when the map div size changes. This approach allows you to handle the resizing "closer" to the map code, rather than trying to rely on window resize events or listening for changes triggered elsewhere in the application.

Obviously the CSS for the map div itself will need to ensure that it resizes in whatever way you want it to. This code snippet will ensure the Leaflet is appropriately updated when that happens.

4
  • 1
    this answer was Very helpful with some edition on angular 8 or above const resizeObserver = new (window as any).ResizeObserver(() => { map.invalidateSize(); }); resizeObserver.observe(mapDiv); Commented Dec 6, 2021 at 14:19
  • perfect answer! saved me a huge headache! Leaflet centering seems good even I transition it's size with grid columns. Commented Mar 20, 2023 at 10:00
  • This answer worked perfectly for me as well! I had Leaflet in a flex container containing the map and a toolbar. Before this fix, the leaflet center would be totally off whenever the toolbar changed size. Now it correctly stays centered! Thanks for this Commented Apr 8, 2023 at 0:25
  • Great answer! The only difference is that I do const mapDiv = document.querySelector('.leaflet-container');
    – asdf
    Commented May 31 at 6:53
20

L.Map.invalidateSize() only informs leaflet map object that its container size has been changed, and therefore is should draw less or more map tiles. It does not actually change any dimensions, e.g. of its containing <div>, and does not move the map. You should do it yourself.

2
  • This should be the best answer ! Thanks
    – eightball
    Commented Aug 29, 2019 at 15:46
  • It does move the map Commented Jun 24, 2021 at 2:50
13

You can use below code after resize that

map.invalidateSize()

https://github.com/Leaflet/Leaflet/issues/690

10

Just call resize window event rather than timing the map to load.

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);

window.dispatchEvent(new Event('resize'));  


// Triggers a window resize 
// Thus your map automatically triggers invalidateSize().
3
  • This solution is really clean and is working great for me so far, are there any drawbacks to this approach? Commented Apr 23, 2021 at 23:26
  • Yes. Say in future if you want to detect real window resize events at the main application level. Above process will create one false signal. That is during it’s time of initiation or depending on how many times you are calling the above process.
    – Srinadh
    Commented Apr 25, 2021 at 0:08
  • Thank you! This solved my problem using ionic 7 inside a tab component
    – Doug
    Commented Apr 1, 2023 at 19:34
6

the accepted answer is a bit hacky in that it relies on the sleep being longer than the transition.

I have found this to work well:

$("body").on($.support.transition.end, '#main-navbar .nav-collapse', function(event){    
    console.log("end of the animation");
});
1
1

Ran into this problem running VueJS, Leaflet 1.2.0. The resizing didn't appear complete as others mentioned above. My solution within VueJS was to call the nextTick function:

  var vm = this
  var container = vm.$refs.container
  vm.mapStyle.width = `${vm.getElementContentWidth(container)}px`
  vm.mapStyle.height = `${vm.getElementContentHeight(container)}px`
  vm.$nextTick(() => {
    if (vm.map) vm.map.invalidateSize()
    if (vm.layerBase) vm.layerBase.redraw()
  })

I believe pure javascript would be

1
  • Thanks the nextTick with invalidateSize solved my issue !
    – Dan
    Commented Jul 9, 2019 at 13:39
0

Coming to this in 2023 with mapbox-gl (v1.13.x) and Vue (v2.7), the answer is similar. I put this code in the mounted() handler:

const resizeObserver = new ResizeObserver(() => {
  this.$nextTick(() => {
    this.map.resize(); // instead of invalidateSize, new API
    // make sure we are zoomed correctly
    // this.boundingBox is a computed property
    this.map.fitBounds(this.boundingBox);
  });
});
resizeObserver.observe(this.$refs.mapContainer);
8
  • how do i keep the map centered on a group of markers as the container changes size (enlarged in my case)? btw, the markers are variables so i cant hard code any coordinates
    – oldboy
    Commented Jan 4 at 1:39
  • 1
    @oldboy - you'd have to make sure the bounding box is computed correctly, but the fitBounds should keep it centered if so!
    – schimmy
    Commented Jan 5 at 13:50
  • can u point me to something that shows how to correctly compute the bounding box given 2 random markers?
    – oldboy
    Commented Jan 6 at 21:50
  • I'm not 100% sure of the best way, but I did it like this: const bounds = bbox(features) const southWest = new mapboxgl.LngLat(bounds[0], bounds[1]); const northEast = new mapboxgl.LngLat(bounds[2], bounds[3]); return new mapboxgl.LngLatBounds(southWest, northEast); where features is an array of geojson feature objects
    – schimmy
    Commented Jan 8 at 15:23
  • hm... the thing is, i dont know the locations/geocoords of one set. (there are two sets, from which a location from each set will be paired with a location from the other set; one set is completely unknown aka user geocoords). and if u set the bounding box like that, wouldnt u have to adjust/update it when, say, even the device orientation changes?
    – oldboy
    Commented Jan 9 at 21:38

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