@factoryfour/frame-client
v1.9.3
Published
Allows a page to communicate over `postMessage` with multiple iframes.
Downloads
36
Maintainers
Keywords
Readme
frame-client
Allows a page to communicate over postMessage
with multiple iframes.
Example
Check out /example/app
and /example/frame
for example usage in React.
Backwards Compatibility
This client can be used within frames that use the older FETCH_DATA
protocol built into
FactoryFour order & task frames:
const client = new FrameClient();
client.on('initial', data => {
// do something with initial data
});
client.on('update', data => {
// do something with updated data
});
// this promise won't be resolved, but the parent window will send 'initial' and 'update'
client.connectParentWindow();
// works as usual
client.send('REQUEST_TYPE', payload);
Usage
Parent page
const iframe = document.getElementById('my-iframe');
const client = new FrameClient();
const origin = 'http://domain-where-iframe-is-hosted.com';
// Get ready to handle messages from the frame
client.on('ok_here_is_the_sum', data => {
console.log('The frame computed the sum: ' + data.sum);
});
// Connect to the iframe
client.connectChildIframe(iframe, origin).then(() => {
// This will be called when the handshake is complete
client.send('here_is_some_initial_data', {a: 3, b: 5});
});
// Make as many iframes as you want!
const iframe2 = document.getElementById('another-iframe');
const client2 = new FrameClient();
client2.connectChildIframe(iframe2, origin);
Child (inside the frame)
const client = new FrameClient();
client.on('here_is_some_initial_data', data => {
const { a, b } = data;
client.send('ok_here_is_the_sum', {sum: a + b});
});
client.connectParentWindow().then(() => {
// This will also be called when the handshake is complete
console.log('Ready to start doing some sums!');
})
Handshake Protocol
This all happens under the hood, and hopefully knowledge of this protocol is not required to use this library in an application.
1. Parent Frame
- Parent Frame creates an iframe, and ends up with a ref to the iframe HTML element
- Parent Frame picks a unique "channel ID" to use for this particular frame
- Parent Frame calls the FrameManager singleton's
register
method- This adds the FrameClient to the global list of FrameClients
- This can happen before or after the frame is loaded
- Parent Frame sends
CHANNEL_ID
just in case the frame is already loaded
2. Child Frame
- The iframe loads. Its contents must be using this same repo! If not, none of this will work
- The iframe knows its parent window (
window.parent
) but does not yet know its channel ID - The iframe sends a
postMessage
towindow.parent
withtype=REQUEST_CHANNEL_IDS
3. Parent Frame
- FrameManager is always listening for
REQUEST_CHANNEL_IDS
. - When it hears this, it sends
type=CHANNEL_ID
with the particular channel ID for EACH frame to ALL frames- We don't know the source of the
REQUEST_CHANNEL_IDS
message, so other frames will unexpectedly get notified of theirCHANNEL_ID
even though they didn't ask for it- Technically, we could deduce the source of the message by comparing it to all the contentWindow references we have stored. But this is brittle because if any of the iframes happens to reload, its contentWindow reference will become obsolete
- Instead, when an iframe reloads, it just sends
REQUEST_CHANNEL_IDS
again and gets back the same channel ID it had before, allowing the "conversation" to continue
- We don't know the source of the
4. Child Frame
- The iframe hears
CHANNEL_ID
and switches to the channel protocol, sending aSYN
with along with the channelId it just received
5. Parent Frame
- Parent frame hears
SYN
on a channel and sendsACK
on that channel - The promise returned by
connectChildIframe
orconnectChildWindow
gets resolved
6. Child Frame
- The iframe hears
ACK
and then it considers its connection established\ - The promise returned by
connectParentWindow
gets resolved - At this point the connection is open and any application-layer message protocol can be carried out on this channel