iframe组件通信问题

208 阅读3分钟

一、父子页面同源的情况

现在有两个不同源的 iframe 嵌套页面,父页面parent.html,子页面child.html,二者代码如下:

ini
 代码解读
复制代码
// parent.html
// ...
<iframe
  id='testIframe'
  name='test'
  src='./child.html'
  frameborder='0'
  scrolling='no'>
</iframe>

<script type="text/javascript">
    function parentConsole(data) {
        console.log(data)
    }
</script>
// ...
javascript
 代码解读
复制代码
// child.html
// ...
<script type="text/javascript">
    function childConsole(data) {
        console.log(data)
    }
</script>
// ...

1. 父页面调用子页面方法

可以通过 iframe 的 id 或者 name 属性拿到 iframe 的 window 对象,然后直接调用子页面方法即可。我们把需要发送给子页面的信息放到方法childConsole里。如下:

ini
 代码解读
复制代码
var iframeDom = document.getElementById('testIframe');
// 需要等iframe加载完成后执行,不然有可能会报错
iframeDom.onload = function () {
    var data = 'hello, child!';
    iframeDom.contentWindow.childConsole(data);
}

ini
 代码解读
复制代码
var iframeDom = document.getElementById('testIframe');
iframeDom.onload = function () {
    var data = 'hello, child!';
    test.window.childConsole(data);
}

2. 子页面调用父页面方法

可以通过 window.top 或者 window.parent 拿到父页面的 window 对象。然后直接调用父页面的方法即可。同样,把需要发给父页面的信息放到方法parentConsole 里。如下:

ini
 代码解读
复制代码
var data = 'hello, parent!';
window.top.parentConsole(data); // 或者使用window.parent.parentConsole(data)也行

二、父子页面跨域的情况

可以通过postMessage来实现通信。

ini
 代码解读
复制代码
otherWindow.postMessage(message, targetOrigin, [transfer]);

其中的参数:

otherWindow
目标窗口。比如 iframe 的 contentWindow 属性

message
将要发送到其他 窗口 的数据。

targetOrigin
目标窗口的域。其值可以是字符串"*"(表示无限制)或者一个 URI。不提供确切的 targetOrigin 将导致数据泄露到任何对数据感兴趣的恶意站点。

现在有两个不同源的 iframe 嵌套页面,父页面http://127.0.0.1:8001/parent.html,子页面http://127.0.0.1:8002/child.html(本地分别对两个html起了两个服务),其中父页面嵌套部分代码如下:

ini
 代码解读
复制代码
// http://127.0.0.1:8001/parent.html
<iframe
  id='testIframe'
  name='test'
  src='http://127.0.0.1:8002/child.html'
  frameborder='0'
  scrolling='no'>
</iframe>

1. 父页面发送信息,子页面接收信息

javascript
 代码解读
复制代码
// http://127.0.0.1:8001/parent.html
// 父页面发送信息
document.getElementById('testIframe').onload = function () {
    test.window.postMessage('hello, child!', 'http://127.0.0.1:8002');
}

// http://127.0.0.1:8002/child.html
// 子页面接收信息
window.addEventListener('message', e => {
    // 通过origin对消息进行过滤,避免遭到XSS攻击
    if (e.origin === 'http://127.0.0.1:8001') {
        console.log(e.origin) // 父页面所在的域
        console.log(e.data)  // 父页面发送的消息, hello, child!
    }
}, false);

2. 子页面发送信息,父页面接收信息

javascript
 代码解读
复制代码
// http://127.0.0.1:8002/child.html
window.top.postMessage('hello, parent!', 'http://127.0.0.1:8001');

// http://127.0.0.1:8001/parent.html
window.addEventListener('message', e => {
    // 通过origin对消息进行过滤,避免遭到XSS攻击
    if (e.origin === 'http://127.0.0.1:8002') {
        console.log(e.origin) // 子页面所在的域
        console.log(e.data)  // 子页面发送的消息, hello, parent!
    }
}, false);

通过postMessagewindow.addEventListener('message', e => { ... })配合使用,我们就能够完成跨域 iframe 父子页面的通信。

当然对于同源的 iframe 父子页面也可以采用postMessage的方式来发送接收信息。

三. iframe未加载完成怎么办?

可以通过window.onload或者document.onreadystatechange来进行监听。

通过子页面的onload来回调中获取.

以下代码写到父页面 ↓↓↓

<iframe src="./child.html"  id="ii" frameborder="0"></iframe>

<script>
  let child = document.querySelector("#ii")//获取子页面
  child.onload = function(){
    console.log(this.contentWindow)
    console.log(this.contentWindow.document)//获取document
  }
</script>

通过父页面的document.onreadystatechange。

//document.readyState这个是document的一个加载状态

1.  interactive 交互 页面dom元素已经加载 但是所有的源文件资源没有加载
2.  complete 所有的资源都加载完毕
3.  速度都比onload快
<script>
  document.onreadystatechange = function(){
    if(document.readyState==="complete"){
      //....
      //操作iframe 传递参数
      child.contentWindow.on('999')
      
    }
  }
</script>

四. 非常见的方法

  • 父窗口可以通过在 iframe 的 src 属性后添加参数来向子窗口传递数据,子窗口可以通过 location.search 或 location.hash 来获取参数。
  • 服务器端转发
  • 浏览器缓存localstroage或sessionstroage。

链接:juejin.cn/post/718944…