iframe-channel
A channel used to communicate between iframe and parent. Support post function.
Install
npm install iframe-channel --save
Demo
Code Examples
Say we have a page, which url is 'http://localhost:9876'. There is an iframe embedded within this page, which url is 'http://localhost:3000'. As follows:
We'll discuss how to conmmunicate between these two page over iframe-channel.
Child request connect
Parent Page
const testIframe = document.createElement('iframe')
testIframe.id = 'test-iframe'
const channel = new Channel({
targetOrigin: 'http://localhost:3000'
})
channel.subscribe('connect', () => {
// have connected
// now send a message, whose type is 'xx' and data is 'hello'
channel.postMessage('xx', 'hello').then((data) => {
// will receive 'hello_hi' from child
expect(data).to.be.equal('hello_hi')
// destroy channel
// Each Channel instance will add 'message' and 'beforeunload' event listener to window
// object. So make sure destroy the instance once it's unused.
channel.destroy()
// destroy iframe
testIframe.parentNode.removeChild(testIframe)
done()
})
})
testIframe.src = 'http://localhost:3000?type=demo_child_request_connect'
document.body.appendChild(testIframe)
Child Iframe
const channel = new Channel({
targetOrigin: 'http://localhost:9876', // only accept targetOrigin's message
target: window.parent // parent window
})
channel.subscribe('xx', (data, message, event) => {
// data === 'hello'
// message == { type: 'xx', data: 'hello' }
return `${data}_hi`
})
channel.connect()
Parent request connect
Parent Page
import Channel from 'iframe-channel'
const testIframe = document.createElement('iframe')
testIframe.id = 'test-iframe'
testIframe.onload = () => {
const channel = new Channel({
targetOrigin: 'http://localhost:3000', // only accept targetOrigin's message.
target: testIframe && testIframe.contentWindow
})
channel.connect().then(() => {
// have connected
// now send a message, whose type is 'xx' and data is 'hello'
channel.postMessage('xx', 'hello').then((data) => {
// will receive 'hello_hi' from child
expect(data).to.be.equal('hello_hi')
// destroy channel
// Each Channel instance will add 'message' and 'beforeunload' event listener to window
// object. So make sure destroy the instance once it's unused.
channel.destroy()
// destroy iframe
testIframe.parentNode.removeChild(testIframe)
done()
})
})
}
testIframe.src = 'http://localhost:3000?type=demo_parent_request_connect'
document.body.appendChild(testIframe)
Child Iframe
import Channel from 'iframe-channel'
const channel = new Channel({
targetOrigin: 'http://localhost:9876' // only accept targetOrigin's message
})
channel.subscribe('xx', (data, message, event) => {
// data === 'hello'
// message == { type: 'xx', data: 'hello' }
return `${data}_hi`
})
Post message before connect
iframe-channel support post message before connect. It will cache all postMessage requests. Then send these requests once the connection is established.
Parent Page
import Channel from 'iframe-channel'
const testIframe = document.createElement('iframe')
testIframe.id = 'test-iframe'
testIframe.onload = () => {
const channel = new Channel({
targetOrigin: 'http://localhost:3000', // only accept targetOrigin's message.
target: testIframe && testIframe.contentWindow
})
// not connect yet
// now send a message, whose type is 'xx' and data is 'hello'
channel.postMessage('xx', 'hello').then((data) => {
// will receive 'hello_hi' from child
expect(data).to.be.equal('hello_hi')
// destroy channel
// Each Channel instance will add 'message' and 'beforeunload' event listener to window
// object. So make sure destroy the instance once it's unused.
channel.destroy()
// destroy iframe
testIframe.parentNode.removeChild(testIframe)
done()
})
channel.connect()
}
testIframe.src = 'http://localhost:3000?type=demo_post_message_before_connect'
document.body.appendChild(testIframe)
Child Iframe
import Channel from 'iframe-channel'
const channel = new Channel({
targetOrigin: 'http://localhost:9876' // only accept targetOrigin's message
})
channel.subscribe('xx', (data, message, event) => {
// data === 'hello'
// message == { type: 'xx', data: 'hello' }
return `${data}_hi`
})
Post function
iframe-channel support post function, including async function.
Parent Page
const testIframe = document.createElement('iframe')
testIframe.id = 'test-iframe'
testIframe.onload = () => {
const channel = new Channel({
targetOrigin: 'http://localhost:3000', // only accept targetOrigin's message.
target: testIframe && testIframe.contentWindow
})
channel.connect().then(() => {
// data can be a function, or an object or an array which contains function.
const data = {
add: function (a, b) {
return new Promise(resolve => {
setTimeout(() => {
resolve(a + b)
}, 1000)
})
},
a: [2, function (a, b) {
return a * b
}],
parentData: 1
}
channel.postMessage('xx', data, {
hasFunction: true,
functionKeys: ['add', 'a[1]']
}).then((data) => {
// will receive 4 from child
expect(data).to.be.equal(4)
channel.destroy()
testIframe.parentNode.removeChild(testIframe)
done()
})
})
}
testIframe.src = 'http://localhost:3000?type=demo_post_function'
document.body.appendChild(testIframe)
Child Iframe
const channel = new Channel({
targetOrigin: 'http://localhost:9876' // only accept targetOrigin's message
})
channel.subscribe('xx', (data, message, event) => {
const childData = 1
const { add, a = [], parentData } = data
const multiply = a[1]
return add(parentData, childData).then(res => {
return multiply(res, a[0])
})
})
API Documentation
Channel Class
Channel
Channel(options) => Param | Type | Description |
---|---|---|
targetOrigin | string |
The channel will only accept targetOrigin's message. |
target | window |
The target window's object that the channel will connect to. |
subscribers | object |
The subscribers object. Such as: { 'conncect': [fun1, fun2], 'xx': [fun1, fun2]}. You can also use 'subscribe' function to register a subscriber after create a Channel instance. |
attemptInterval | number |
The interval between reconnect attempts. Default 1000ms. |
maxAttempts | number |
The max number of times we'll try to reconnect for. Default 10 times. |
undefined
subscribe(type, fun?) => Register a function to subscribe a specific type of message.
channel.subscribe('xx', (data, message, event) => {
// data === 'hello'
// message == { type: 'xx', data: 'hello' }
return `${data}_hi`
})
// fun can be a function array
channel.subscribe('xx', [fun1, fun2])
// type can be an object.
channel.subscribe({
'xx': [fun1, fun2],
'ff': fun3
})
undefined
unsubscribe(type?, fun?) => Remove subscribers.
// remove all
channel.unsubscribe()
// remove 'xx' type's subscribers
channel.unsubscribe('xx')
// remove 'xx' type's fun1 subscriber
channel.unsubscribe('xx', fun1)
// remove 'xx' type's fun1 and fun2 subscriber
channel.unsubscribe('xx', [fun1, fun2])
// remove 'xx' type's fun1 and 'xx1' type's fun3 and fun4 subscriber
channel.unsubscribe({
'xx': fun1,
'xx1': [fun3, fun4]
})
Promise
postMessage(type, data, opts?) => Post a specific type of message with data.
opts | Type | Description |
---|---|---|
hasFunction | bool |
Does the data contain a function or it is a function? |
functionKeys | string[] |
A path string array. iframe-channel will use lodash/get to fetch the function in the specific path. If hasFunction is true but functionKeys is undefined, iframe-channel will traverse the data's each field to collect functions. This may have performance issues. |
channel.postMessage('xx', 'hello').then(data => {
console.log(data)
}).catch(err => {
console.log(err)
})
Promise
connect() => Connect to the target.
channel.connect().then(() => {
// postMessage
}).catch(err => {
console.log(err)
})
undefined
clearQueue() => Clear postMesasge queue. Channel will queue postMessage requests that was sent before connect.
channel.clearQueue()
undefined
destroy() => destroy a Channel instance. Including:
- clear subscribers.
- clear postMessage queue.
- remove window's message event listener.
- reset inner state.
channel.destroy()
License
Copyright (c) 2019-present Chunlin He