Iframe Messaging
The Mappedin viewer provides a messaging API for communication between an <iframe>
and its parent window. This allows the parent window to send messages to update the map and the map to send messages back to the parent window, alerting it of changes to the map's state.
Events
When certain events occur in the viewer, the viewer will post a message using postMessage() to the parent window containing data with the following structure.
{ type: "<eventName>", payload?: "<eventData>" }
The viewer currently informs the parent window of the following events.
App Loaded Event
The viewer sends an app-loaded
message after it has initialized within the iframe.
The loading spinner has not necessarily gone away, but the app is ready to receive messages from the parent window.
{
"type": "app-loaded"
}
State Changed Events
When state
, floor
, selected location
, or departure
location changes, viewer sends a state-changed
message.
state
is either/
or/directions
indicating whether the map is in the default or directions state.floor
is the ID of the current floor.location
is an array of ID strings corresponding to all selected locations. If there is no selected location, this will beundefined
.departure
is an array of ID strings corresponding to all chosen departure locations. If there is no departure location, this will beundefined
. Only the closest departure will be chosen as the starting point for navigation.
{
type: "state-changed",
payload: {
state: "</ | /directions>",
floor: "<floorId>",
location: "<locationId>"[] | undefined,
departure: "<locationId>"[] | undefined,
}
}
Sending Commands
The parent window can also issue commands by posting messages to the <iframe>
containing the viewer. Command messages should use the following structure.
{ type: "<eventName>", payload?: "<eventData>" }
The viewer will listen for these commands and react accordingly. The viewer currently understands the following commands.
Set State Command
Use the set-state
command to update the application state
, floor
, selected location
, or departure
location. The set-state
command allows setting any or all of these values.
state
should be either/
or/directions
indicating either default or directions state.floor
is the ID or name of the desired floor.location
is an ID or name string (or an array of ID or name strings) corresponding to all locations to be selected.departure
is an ID or name string (or an array of ID or name strings) corresponding to all locations that could be departures. Only the closest departure will be chosen as the starting point for navigation.
{
type: "set-state",
payload: {
state?: "</ | /directions>",
floor?: "<floorId | floorName>",
location?: "<locationId | locationName>" | <locationId | locationName>"[],
departure?: "<locationId | locationName" | "<locationId | locationName>"[],
}
}
Deep Linking Within an Iframe
Refer to the Deep Linking documentation for a list of deep link parameters.
When the viewer is embedded within an <iframe>
, it cannot access the parent window's URL due to CORS policies. This means that deep linking does not work with the embedded map unless the viewer's messaging capabilities are implemented. To ensure a smooth experience out of the box, when sharing a link using the built in Sharing button, the shared URL will always be to the full viewer even if the map is currently embedded. Users can also always open the full viewer using the button in the top right.
To support deep linking within an embedded map, communication between the parent window and the viewer must be implemented. This can be done using the messaging and events APIs described above.
The following example demonstrates how to implement deep linking with a map embedded in an iframe.
/**
* Deep Linking
*/
// Set the initial iframe URL based on the hash
const hashParams = new URLSearchParams(window.location.hash.slice(1));
const state = hashParams.get('state') ?? '/';
const location = hashParams.get('location');
const departure = hashParams.get('departure');
const floor = hashParams.get('floor');
const url = new URL(`${IFRAME_BASE_URL}${state}`);
url.searchParams.set('embedded', 'true');
if (location != null) {
url.searchParams.set('location', location);
}
if (departure != null) {
url.searchParams.set('departure', departure);
}
if (floor != null) {
url.searchParams.set('floor', floor);
}
iframe.src = url.toString();
// Handle messages from the iframe to update the URL
window.addEventListener('message', (event) => {
if (event.data.type === 'state-changed') {
const { state, location, departure, floor } = event.data.payload;
let hashParams = new URLSearchParams();
if (state != null) {
hashParams.append('state', state);
}
if (location != null) {
hashParams.append('location', location.join(','));
}
if (departure != null) {
hashParams.append('departure', departure.join(','));
}
if (floor != null) {
hashParams.append('floor', floor);
}
window.location.hash = hashParams.toString();
}
});
Example
The CodeSandbox below implements all of the concepts described in this guide. Note how the parent window's URL is updated when the map's state changes.