A small (dependency-free) library for 2-way iframe communication.
You are embedding an iframe where you would like to communicate with the following facilities:
- From your parent page, you need to use
iframe.contentWindow.postMessage
- From your iframe, you need to subscribe to these messages using
window.addEventListener('message', (ev) => ...)
- And vice versa, sometimes at the same time
This library allows you to define a protocol for this type of communication
supporting both fire-and-forget (tell
) semantics, as well as request/response
(ask
) semantics. It provides connector constructors that will facilitate the
sending and receiving of these messages across parent and iframe windows.
npm install protoframe --save
import { ProtoframeDescriptor } from 'protoframe';
const cacheProtocol: ProtoframeDescriptor<{
getFoo: {
body: { key: string };
response: { value: string };
};
setBar: {
body: { key: string; value: string };
};
}> = { type: 'cache' };
The ProtoframeDescriptor
defines 2 elements of your protocol:
- The message types. The type argument is a structural type that defines the
message types as its top level key. For each message type, it defines the
body
type of the payload (forask
andtell
requests), and an optionalresponse
types (forask
requests). Anask
message must define aresponse
key, atell
message must not - The value of the descriptor (
{type: 'cache'}
) defines a type key for the protocol, which is used to key on specific types of messages to listen to between the pages
The connector allows 2-way communication between a page and its iframe. For example, we have a page that wishes to use an iframe as a "cache" server:
Iframe:
import { ProtoframePubsub } from 'protoframe';
// Will hold our cache state
const data: { [key: string]: string } = {};
// The connector. This function creates a connector that listens for messages
// on `window` and sends messages and responses over `window.parent`
const cache = ProtoframePubsub.iframe(cacheProtocol);
// Implement our handlers for the ask and tell requests defined in the protocol
cache.handleTell('setBar', ({ key, value }) => (data[key] = value));
cache.handleAsk('getFoo', async ({ key }) => {
const value = key in data ? data[key] : null;
return { value };
});
Parent:
import { ProtoframePubsub } from 'protoframe';
const iframe = document.getElementById('myCacheServerIframe');
const client = ProtoframePubsub.parent(cacheProtocol, iframe);
// Wait for the iframe page to load and for the connector to be instantiated
ProtoframePubsub.connect(client).then(() => {
client.tell('setBar', { key: 'my key', value: 'my value' });
// value = { value: 'my value' }
const value = await client.ask('getFoo', { key: 'my key' });
});