Is the element referenced in
useRef
somehow not an actualHTMLElement
instance?
No, it is.
But (as you’ve already known it), Mapbox GL JS (v2.15.0) requires that the container, when supplied as an element, be an instance of window.HTMLElement
. ( note the window.
which means the parent window )
And the thing is, on the FSE/Site editor page, the block’s editor script which initializes the Mapbox map is in the parent window, but then div#map
is inside a child window which is an iframe
named editor-canvas
, hence:
-
document.getElementById('map')
, wheredocument
refers towindow.document
, returned anull
. -
The map div is no longer an instance of
window.HTMLElement
and instead, it’s now an instance of the iframe’scontentWindow.HTMLElement
. I.e.-
mapContainer.current instanceof HTMLElement
(which is the same asmapContainer.current instanceof window.HTMLElement
) returnsfalse
. -
mapContainer.current instanceof document.querySelector( 'iframe[name="editor-canvas"]' ).contentWindow.HTMLElement
returnstrue
.
-
So for the time being, as you commented, you would need to use SSR (i.e. server-side rendering) instead.
I’ve created a GitHub repo with a working example where my edit
function returns an element with an iframe which renders the Mapbox map, as you could see below: (I used Firefox)
And here’s my edit function: ( see full code here )
const Edit = ( { attributes, setAttributes } ) => {
const { accessToken } = attributes;
const mapContainer = useRef( null );
const [ mapLoaded, setMapLoaded ] = useState( false );
const onMapFrameLoad = () => {
let map;
// Note: getMap is defined on the SSR page. It's a custom function which
// returns the Mapbox instance for the map initialized on that SSR page.
try {
map = mapContainer.current.contentWindow.getMap();
} catch ( error ) {
console.log( 'Error accessing the Mapbox\'s map instance!', error );
return;
}
map.on( 'load', () => {
setMapLoaded( true );
console.log( 'map ready', map );
} );
};
return (
<div { ...useBlockProps() }>
{ /* In this demo, I'm deliberately not using <InspectorControls>. */ }
<fieldset>
<TextControl
label="Access Token"
value={ accessToken }
onChange={ ( value ) => setAttributes( { accessToken: value } ) }
/>
</fieldset>
{ ( accessToken && ! mapLoaded ) && <i>Loading map..</i> }
{ accessToken && (
<iframe
src={ ssrPageUrl( accessToken, 300 ) }
width="100%"
height="300"
style={ { border: 0, overflow: 'hidden' } }
id="map-frame"
onLoad={ onMapFrameLoad }
ref={ mapContainer }
></iframe>
) }
</div>
);
};