Skip to main content

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 be undefined.
  • departure is an array of ID strings corresponding to all chosen departure locations. If there is no departure location, this will be undefined. 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.