mapbox-gl-js version: 0.24.0
<div id="map"></div>
with JS and CSS - CDN references in header#map { position:absolute; top:0; bottom:0; width:100%; }
Map loads with 100% height
Map (canvas) height is 300px
Unfortunately we can't debug your Wordpress site for you. Looking at the description, the issue is most probably with your application CSS, not with Mapbox GL JS.
Thanks for the reply. Using Leaflet,js to load Mapbox styled map solves the problem.
I would really like to work just with mapboxgl-js. I am able to load the map and it resizes correctly within the container when I resize using the window handle but the canvas does not resize when I maximize the window. The container element resizes to full height but the canvas element retains the height of the previous height of the container. How do I trigger map.resize() when the window is maximised?
Just putting everything together for this thread now. It is now working without using Leaflet. Just had to call map.resize() on $(window).resize() function to redraw the map. All good. Thanks for everything. CLOSED!
i have the same issue.
@dshakya could you tell, where to put that code?
I tried this at the end: jQuery(window).resize(function(){map.resize()});
Unfortunately we can't debug your Wordpress site for you. Looking at the description, the issue is most probably with your application CSS, not with Mapbox GL JS.
@mourner fair enough. Is there any list of css requirements for container?
I see that mapbox-gl relies on some css, so there is a chance that applying some custom styles to container element can break mapbox, like I saw drag broken by css. I see this 300px issue. I suppose that 300px is default size of canvas if mapbox-gl failed to get size of container.
It looks like the 300px default height comes from here.
I would assume your container element has an offsetHeight
of 0, that would cause the 300px default height to be used.
I'm running into the same issue using mapboxgl with react. @mfogel pointed out that line of code, so I set the canvas' container, mapboxgl-canvas-container
, to a pixel height and canvas remained 300px. This is after trying map.resize()
, trying different layout options from position relative parent/absolute child to flex-box, etc. any pointers?
This solved it for me!
On the react component:
<div
id="map"
style={{
position: 'absolute',
top: 0,
bottom: 0,
width: '100%',
height: '100%',
}}
/>
On a css file:
.mapboxgl-canvas {
left: 0;
}
This is not a problem with Wordpress, jQuery, or any application. The issue is that Mapbox is resizing when CSS has changed the size of the container where the <canvas>
is located.
This can happen, for example, when a calc( var(--some-var) ... )
expression is used to size the mapbox container, in which case the sizing may not always happen on a window resize.
This ideally needs to be fixed in Mapbox, because there are cases where it is impossible for the end user to reliably know when or how to call map.resize()
aside from polling or in newer browsers using ResizeObserver
to detect size changes on the canvas container.
The best solution is for Mapbox to use size polling for older browsers, and ResizeObserver
in newer browsers, so that Mapbox can do what it guarantees it will.
I also have this issue. I have spent several hours trying to get this working today and so far, no dice!
I'm using VueJS with the vanilla "mapbox-gl" from npm rather than the "mapbox-gl-vue" variant.
The only thing that appears to get the canvas to the correct size is when I have set the trackResize: true
option on a new instance, and then resize the window manually.
None of the options using inline styles or the suggested class styling have worked for me.
We don't maintain any framework specific plugins but I've used this library with Vue successfully in the past. If you're using height: 100%
and it's defaulting to 300px still, my guess is that the container does not have an offset height. Even if it appears to have an offset height when inspecting it, it's possible that it does not have an offset height when the map is instantiated. I'd suggest making sure that you're calling new mapboxgl.Map()
in Vue's mounted
lifecycle method to ensure that the container has been properly laid out by the browser first.
@trusktr Sorry for the late reply. If you feel this is a bug in the library, can you create a minimal example recreating the issue in JSBin?
Thank-you for the response Ryan - it appears I have solved my issue!
Although I am creating the map in vue's mounted lifecycle hook, I omitted to mention that the map is actually displayed in a popup modal when the user clicks a button.
My reasoning for the previous behaviour is this: at the point where the modal is mounted it is still hidden, and mapbox doesn't know how big the container will be. Therefore the canvas defaults to the 300px size. Although I knew that I needed to resize the map, it wasn't working when my updateMap
method attached to the click handler was being called. My solution was to add a short delay after the modal has been triggered before calling this.map.resize
, allowing time for the modal container's size to be determined.
Apologies for not working this out before!
If it helps anyone, I solved this issue in Angular by moving the setup code from ngOnInit()
to ngAfterViewInit()
. The map container div had a height of 50vh
which must not have been calculated by the time the map was drawn. Thanks to @trusktr for the hint that it had to do with calculated sizes (as opposed to pixel sizes)
I encountered the same issue using the mapbox-gl-vue
library for Vue. While @juansalvatore's solution did not work for me, triggering a resize event when the component mounted did do the trick, although with a flicker. So far it seems that setting the height and width directly on the mapbox
element allows it to initialize properly, as @ryanhamley and others have suggested.
_containerDimensions() {
let width = 0;
let height = 0;
if (this._container) {
// container width and container height set in here ,
// if css not calculated clientWidth-clientHeight is 0 and result 400 and 300
width = this._container.clientWidth || 400;
height = this._container.clientHeight || 300;
// 0, 0 , 400, 300
console.log( this._container.clientWidth,this._container.clientHeight,width,height)
}
return [width, height];
}
When this code executed, this._container.clientWidth and this._container.clientHeight equal to 0.
Because your css didn't calculated when _containerDimensions() function is executed.
In my situation, react code splitting cause that problem. Css loaded / calculated very late.
And that's, how to solve
componentDidMount() {
// first ensure #map-field mount (componentDidMount)
// load css
// create mapbox
import('./MapField.css').then(() => {
mapboxgl.accessToken = 'token';
this.mapBox = new mapboxgl.Map({
container: 'map-field',
style: 'mapbox://styles/mapbox/satellite-streets-v10',
// ...other settings
});
});
}
render() {
return (
<div id="map-field"></div>
);
}
// MapField.css'
#map-field{
height: 100vh; // for my situation
width: 100%;
position: relative;
z-index: 2;
}
It's not mapbox-gl-js ,react,vue problem. Ensure your css loaded and calculated before init mapboxgl.Map.
If not possible above
// bad solution :/
this.map.on('load', () => {
this.map.resize();
});
This issue is still present, and not specifically related to any platform. This should be reopened.
I have the same issue in vue project.The reason is that i new map in a router page which is lazy-loaded when the route is visited.
{
path: '/mapView',
name: 'mapView',
component: () => import( './views/mapView.vue')
}
how it solved:
{
path: '/mapView',
name: 'mapView',
component:mapView
}
Anyone trying to get this fixed via nextJS implementation, i created a <head>
component and stuffed a jsx global style in there:
import NextHead from 'next/head'
const Head = props => (
<div>
<NextHead>
<meta charSet="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://api.mapbox.com/mapbox-gl-js/v0.51.0/mapbox-gl.css" rel="stylesheet" />
<title>shitty-balls-in-my-mouth</title>
</NextHead>
<style jsx global>{`
.mapboxgl-canvas {
left: 0;
}
`}</style>
</div>
)
Using Vue, I tried the approach suggested @hlthi, by waiting to load the map until after the CSS has been loaded, but could not get it to work. The alternate solution he suggested did end up working, so I will use it for now.
self.mapview.on('render', function() { self.mapview.resize(); });
This has nothing to do with mapbox. To my experience, these issues emerge from other css or surrounding elements that prevent the container for mapbox from being properly initialized. In this case, mapbox reverts to default 300px height. In vue, for example, I could solve this issue by adding a flexbox style to the outer div of the home view:
<template>
<div class="box">
<v-content >
...
</v-content>
</div>
</template>
<style>
.box {
display: flex;
flex-flow: column;
height: 100%;
}
</style>
and then putting the mapbox-container in a vue-container with fluid and fill-height enabled:
<template>
<v-container fluid fill-height>
<v-layout column>
<mapbox-component/>
</v-layout>
</v-container>
</template>
I've been struggling with this for a few days and just figured it out! In my case it was because of a combination of an SPA and Webpack injecting styles with JS. In both cases they caused Mapbox to do its _container.offsetHeight
query _before_ the CSS had even entered the DOM. To fix the Webpack issue I used the mini-css-extract-plugin, which is a good idea anyway as it's used for bundling the CSS for production .
The SPA problem was that I wasn't waiting for the correct lifecycle method when the virtual element had been fully inserted into the DOM.
For anyone using Angular and Ionic - I have the map in its own component in which you need to use the host pseudo tag to make sure the height and width on that component are set:
Component Scss:
`:host {
display: block;
height: 100vh;
width: 100vw;
}
#map, {
height: 100vh;
width: 100vw;
}`
You will also need to make sure you initialise the map (as stated above) within the ngAfterViewInit - however for me it simply didn't work. However after adding a timeout callback (below) it started working. The resize() approach was really ugly and would resize in front of the user.
`ngAfterViewInit() {
this.cd.detectChanges();
setTimeout(() => {
this.initialiseMap();
}, 0);
}`
And my component HTML was simply:
<div id="map"></div>
I'm using the Ionic 4 framework and this worked for me:
async buildMap() {
console.log('Building map...');
const position = new mapboxgl.LngLat(10, 10);
this.map = new mapboxgl.Map({
container: 'map-container',
style: 'mapbox://styles/mapbox/satellite-streets-v10'
center: position,
zoom: 10,
scrollZoom: false
});
// On map load
this.map.on('load', (event) => {
this.map.resize();
});
}
The trick was to call for resize as soon as the map loads. Note that my map <div id="map-container">
is set to height: 100%
in the CSS. Calling resize()
forces the map canvas to match that rather than use it's default.
If it helps anyone, I solved this issue in Angular by moving the setup code from
ngOnInit()
tongAfterViewInit()
. The map container div had a height of50vh
which must not have been calculated by the time the map was drawn. Thanks to @trusktr for the hint that it had to do with calculated sizes (as opposed to pixel sizes)
on my case jquery init works ok
$(function () {
//alert('hola mundo');
map.resize();
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(function (position) {
lat = position.coords.latitude;
lng = position.coords.longitude;
});
};
});
The problem is that the map element if rendered with display: none
it will set the offsetHeight
to null
which leads to the canvas using the default dimensions (400x300).
Yet, by using visibility
it will have an offsetHeight which can be done using the following approach...
Set your maps initial style to:
#your-hidden-map-container { visibility: hidden; position: absolute; top: -10000; }
Then when you are displaying the map, use this style:
#your-visible-map-container { visibility: visible; position: relative; top: 0; }
Note: set the styles on the wrapper/container div, not on the mapbox container directly, the reason top is being used it to prevent a scrollbar from appearing
easiest fix of them all: write the CSS for the height/width inline
example
<div style="width: 100vw; height: 100vh;" id="map"></div>
The solution,
using 'height: 100vh' and width: '100%'
<div
ref={el => this.mapContainer = el}
style={{
position: 'absolute',
top: 0,
bottom: 0,
width: '100%',
height: '100vh',
}}
/>
@doyle-mark im using Vue and it worked for me thanks!
@ryanhamley
I'd suggest making sure that you're calling new mapboxgl.Map() in Vue's mounted lifecycle method to ensure that the container has been properly laid out by the browser first.
This still needs
<div
id="map"
style="height: 100%;"
></div>
Could it be made 100% by default, so it works out of the box? It properly fits it's container.
The following worked for me:
CSS:
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
Component:
```
public ngOnInit() {
mapboxgl.accessToken = environment.mapbox.accessToken;
this.map = new mapboxgl.Map({
container: 'map',
style: this.style,
zoom: 1,
});
this.map.addControl(new mapboxgl.NavigationControl());
setTimeout(() => this.map.resize(), 0);
}
The following worked for me:
CSS:
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
Component:
public ngOnInit() { mapboxgl.accessToken = environment.mapbox.accessToken; this.map = new mapboxgl.Map({ container: 'map', style: this.style, zoom: 1, }); this.map.addControl(new mapboxgl.NavigationControl()); setTimeout(() => this.map.resize(), 0); }
The trick there is using setTimeout()
to resize immediately. That works for me, but it seems hacky. Neither ngAfterContentInit nor ngAfterViewInit _alone_ worked for me as was brought up above. I basically have a 100% width and height div
in an Ionic ion-content
, no additional nested divs or styling. This behaviour probably has something to do with Ionic hiding all its elements with visibility: hidden
until ion-content
becomes .hydrated
, which applies visibility: inherit
.
The problem is that the map element if rendered with
display: none
it will set theoffsetHeight
tonull
which leads to the canvas using the default dimensions (400x300).Yet, by using
visibility
it will have an offsetHeight which can be done using the following approach...Set your maps initial style to:
#your-hidden-map-container { visibility: hidden; position: absolute; top: -10000; }
Then when you are displaying the map, use this style:
#your-visible-map-container { visibility: visible; position: relative; top: 0; }
Note: set the styles on the wrapper/container div, not on the mapbox container directly, the reason top is being used it to prevent a scrollbar from appearing
for those who use the ionic map it must be loaded after the html, otherwise it will have the default size of 400x300, you must start the map in the ionViewDidEnter method. If they do it in the ngOnInit method it won't work.
more information lifeCycle ionic
sorry for my English.
this is my example code, modify it according to yours
mapa: Mapboxgl.map
ionViewDidEnter(){
(Mapboxgl as any).accessToken = environment.mapboxKey
this.mapa = new Mapboxgl.Map({
container: 'mapa', // container id
style: 'mapbox://styles/mapbox/streets-v11', // stylesheet location
center: [-74.5, 40], // starting position [lng, lat]
zoom: 9 // starting zoom
});
}
Most helpful comment
This is not a problem with Wordpress, jQuery, or any application. The issue is that Mapbox is resizing when CSS has changed the size of the container where the
<canvas>
is located.This can happen, for example, when a
calc( var(--some-var) ... )
expression is used to size the mapbox container, in which case the sizing may not always happen on a window resize.This ideally needs to be fixed in Mapbox, because there are cases where it is impossible for the end user to reliably know when or how to call
map.resize()
aside from polling or in newer browsers usingResizeObserver
to detect size changes on the canvas container.The best solution is for Mapbox to use size polling for older browsers, and
ResizeObserver
in newer browsers, so that Mapbox can do what it guarantees it will.