Iframe通信

Posted CamilleZJ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Iframe通信相关的知识,希望对你有一定的参考价值。

 跨域的种类

一般有两种形式的跨域问题:

 ①使用XmlHttpRequest(XHR)或者使用AJAX发送的POST或者GET请求。这种形式的跨域是:前端页面与后端进行的跨域请求。

②父子页面之间进行的DOM操作(父子窗口之间的document操作)。这种形式的跨域是:前端页面与前端页面之间的通信或者相互操作的形成跨域。(本文主要讲这种)

iframe通信方式:

  • 同域直接通过iframe的contentWindow 和parent去操作相应的窗口内的window.document....
  • 主域不同,设置document.domain就可以如上操作
  • 跨域及不跨均可应用:window.name、location.hash、postMessage和onmessage

访问iframe: contentWindow
访问父级:parent
访问顶级:top

1、不跨域:

父:

<body> 
    <textarea id="message">这是高层的密码!</textarea><br/> 
    <button id="test">看看员工在说什么</button><br/><br/><br/>
    员工们:<br/> 
    <iframe src="b.htm" width="500" height="300" id="iframe"></iframe> 
    <script> document.getElementById("test").onclick = function() 
    alert(document.getElementById("iframe").contentWindow.document.getElementById("message").value); 
  
    </script> 
</body> 

子:

<html xmlns="http://www.w3.org/1999/xhtml">
<head> 
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>JSONP方式</title><script type="text/javascript" src="/js/jquery-1.5.1.min.js"></script> </head>
<body> 
    <textarea id="message">这是员工的密码!</textarea><br/>
    <button id="test">看看领导在说什么</button><br/> 
    <script> document.getElementById("test").onclick = function() alert(parent.document.getElementById("message").value);  </script>
</body>
</html>

2、主域相同、子域不同

使用document.domain=主域名

document.domain设置规则:

  •  document.domain只设置域名或者IP地址。即:没有协议(http/https),没有端口。
  • 如果访问的地址是域名形式,则document.domain只能设置为当前域名下的同级或者父级域名,并且不能设置为顶级域名,也不可以设置为其他形式的域名(不是当前域名的父级或同级)。

例如:当前访问地址为:https://mp.csdn.net/mp_blog/creation/editor/121540023(当前页面)。可以打开Chrome的控制台(F12)找到console的tab。默认情况下,我们输入document.domain会显示‘csdn.net’。其中csdn.net就是https://mp.csdn.net的父级域名。我们也可以设置为mp.csdn.net。因为mp.csdn.net是mp.csdn.net的同级域名。

但是我们不能设置顶级域名,即document.domain='net',就会提示错误信息。也不能设置其他的域名形式。即document.domain='csdn1.net'或者document.domain='baidu.com'。提示也说得很清楚。必须是mp.csdn.net的一个后缀形式。

  • 如果访问地址是IP的形式,则document.domain只能设置一样的IP地址。

 注意:根据上面的设置规则,在使用document.domain的方式时,页面之间必须有相同的父级域名。否则这种方式将无法进行操作。

a.html (http://a.xxx.com/js/crossdomain/demo/a.htm)

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>A</title>
</head>
<body>
<textarea id="message">这是高层的密码!</textarea><br/>
<button id="test">看看员工在说什么</button><br/><br/><br/>员工们:<br/>
<iframe src="http://b.xxx.com/js/crossdomain/demo/b.htm" width="500" height="300" id="iframe"></iframe>
<script>
    document.domain = "jiaju.com";

   document.getElementByI d("test").onclick = function()
        alert(document.getElementByI d("iframe").contentWindow.document.getElementByI d("message").value);
    
</script>
</body>
</html>

b.html ((http://b.xxx.com/com/js/crossdomain/demo/b.htm ))

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>JSONP方式</title>
<script type="text/javascript" src="/js/jquery-1.5.1.min.js"></script>
</head>
<body>
<textarea id="message">这是员工的密码!</textarea><br/>
<button id="test">看看领导在说什么</button><br/>
<script>
    document.domain = "jiaju.com";
    document.getElementByI d("test").onclick = function()
        alert(parent.document.getElementByI d("message").value);
    
</script>
</body>
</html>

 两个域都设置:document.domain=‘jiaju.com’

3、主域不同

1)location.hash

location.hash原理:
1、动态改变location.hashiframe不会重载
2、无论跨域与否,iframe内可以获取自己的location.hash
3、只要域名相同就能通信,即使ABC三层嵌套

a.html(http://www.aaa.com/demo/cross/iframe03/a.htm)嵌套了b.html(http://www.bbb.com/demo/cross/iframe03/b.html)

//父页面通过hash像子页面传值
var iframe = document.getElementByI d("iframe")
修改iframe.src改变其hash

//子页面监听hash改变,获取hash上的值:window.location.hash


//子页面=》父页面:要知道iframeid
var hash_url = window.location.hash,
      datas = hash_url.split("#")[1].split("&"),
      data = ;

for(var i = 0;i<datas.length;i++)
    var t = datas[i].split("=");
    data[t[0]] = decodeURIComponent(t[1]);

document.domain = "jiaju.com";
switch(data["JJtype"])
    
        case "height":
            trytop.window.document.getElementByI d(data["iframeID"]).height = data["height"];catch(e)
            break
        case "width":
            trytop.window.document.getElementByI d(data["iframeID"]).width = data["width"];catch(e)
            break
        case "callback":
            trytop.window[data["fn"]].call(document,data);catch(e)
            break
        default:
    

location.hash缺点
1、传递数据量有限
2、不太安全

2)window.name

location.hash缺点
1、传递数据量有限
2、不太安全

window.name

window.name 是什么:
name 在浏览器环境中是一个全局window对象的属性
当在 iframe 中加载新页面时,name 的属性值依旧保持不变
name 属性仅对相同域名的 iframe 可访问
window.name 的优势:

  • 数据量更大(2M)
  • 更安全
  • 可传递多种数据格式

window.name 的劣势:

  • 只适用于隐藏iframe的情形

原理(1) :
A创建iFrame B,把要传递的数据放入window.name

打开Chrome的控制台,当前地址为:www.baidu.com。你在console的控制台中输入window.name='moxiao',然后将当前页面的地址栏的地址改为:mai.qq.com,然后在console的控制台中打印window.name将会显示:‘moxiao’

跨域iframe怎么调用父页面方法

 若父页面: parent.html;嵌在父页面的子iframe页面:child.html

1)同域时 iframe 调用父页面的JS方法

在同域的情况下,子iframe页面可以很方便地直接调用父页面定义的JS方法:window.parent.fn(); 或者 window.top.fn();

  • window.self: 当前窗口自身的引用
  • window.parent: 上一级父窗口的引用
  • window.top: 最顶层窗口的引用

当页面中不存在 iframe 嵌套时,则 window.self, window.parent, window.top 三者均是当前窗口自身的引用。

2)不同域时 iframe 调用父页面的JS方法

不同域时上面方式调用,不报跨域错误

postMessage 的发送与接收,Window.postMessage 是 HTML5 提供的一个跨域解决方案

1、发送:otherWindow.postMessage(message, targetOrigin, [transfer]);

  • otherWindow:其他窗口的一个引用,如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames; 
  • message: 将要发送到其他 window的数据;
  • targetOrigin: 通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串”*”(表示无限制)或者一个URI。

2:接收:

//event.data :传递过来的信息,也就是 postMessage 中发出的 message;
//event.origin: 发送信息页面的域名,包括协议和端口号。
window.addEventListener("message", function(event)
    var data = event.data;  
    // 判断域名
    if(event.origin == 'http://192.168.1.237')
        //doSomething()
    
);
  • 父页面像子页面通信:ifram的contentWindow.postMessage
  • 子页面像父页面通信:window.parent.postMessage或window.top
  • 接收的话直接window监听onmessage事件即可

实现:

  • a.com 域名下的父页面 parent.html 定义了功能函数 sayHi();父页面 parent.html 中嵌套了子 iframe 页面 child.html(在域名b.com域名下) 。
  • 在child.html中引起触发、执行父页面定义的 sayHi()方法。
  • 在child.html中向父页面请求获取数据 uname 值。

1) child.html代码:

<script type="text/javascript" src="sdk_child.js"></script>
<script type="text/javascript">
    // 1)触发父页面定义的方法
    window.SDK.sayHi(msg: 'hi');
    
    // 2)向父页面请求获取数据 uname
    var uname = '';
    window.SDK.getUname();
    setTimeout(function()
        uname = window.SDK.uname;
        //doSomething(uname);
    , 200);
    // 备注:发送请求后,需要延时接收返回的数据
</script>

2) child.html 中引入的js文件 sdk_child.js 代码:

;(function()
    var sdk = window.SDK || ;
    sdk.uname = null;

    //发送
    sdk.getUname = function()
        window.top.postMessage(
            action: "getUname"
        , "*");
    sdk.sayHi = function(info)
        window.top.postMessage(
            action: "sayHi",
            info: 
                msg: info.msg
            
        , "*");
    //接收
    window.addEventListener("message", function(e)
        var res = e;
        var action = res.data.action;
        var info = res.data.info;
        //判断域名
        if(res.origin == 'a.com')
            switch (action) 
                case 'getUname' :
                    sdk.uname = info;
                    break;
                default :
                    return
            
        
    );
    //写入window
    window.SDK = sdk;
)();

3) parent.html 中引入的js文件 sdk_parent.js代码:

;(function()
    var iframecont = document.getElementById('gameIframe').contentWindow;
    var sdk =
        getUname: function()
                var info = Tool.pareUrl(location.href);
                iframecont.postMessage(action: 'getUname', info: 'zhangsan', 'b.com');
            ,
        sayHi: function(info)
                alert(info.msg);
        ,
    ;

    //监听接收
    window.addEventListener("message", function(e)
        var res = e;
        var data = e.data;
        var info = e.data.info;
        if(true)
            switch (data.action) 
                case 'sayHi' :
                    sdk.sayHi(info);
                    break;
                case 'getUname' :
                    sdk.getUname();
                    break;
                default :
                    return
            
        
    );
)();

iframe通信

一.基础信息

简介:

    iframe作为一个html标签,可以让嵌入任何的html网页

属性:

    1.frameborder:是否显示边框,1(yes),0(no)
    2.height:height属性指定了iframe的像素高度,建议使用css設置。
    3.width:width属性指定了iframe的像素宽度,建议使用css設置。
    4.name:iframe的名称,window.frames[name]时专用的属性。
    5.scrolling:iframe是否滚动。yes,no,auto。
    6.src:iframe的网址。
    7.sandbox:控制iframe內的权限(html5新功能),相关资料:[Play safely in sandboxed IFrames - HTML5 RocksHow to Safeguard your Site with HTML5 Sandbox](https://web.dev/sandboxed-iframes/) [| Microsoft Docs](https://docs.microsoft.com/en-us/previous-versions/msdn10/hh563496%28v=msdn.10%29)

缺点:

    1、iframe会阻塞主页面的Onload事件;
    2、搜索引擎的检索程序无法解读这种页面,不利于SEO;
    3、iframe和主页面共享连接池,而浏览器对同域的连接有限制,所以会影响页面的并行加载;
    4.因为iframe等于打开一个新的网页,所有的JS/CSS全部加载一遍,内存会增加;

二.iframe非跨域通信

有一点很重要,iframe是可以给name 属性的;给上name 属性可以省下一些代码;

父调子

// 父页面
<button id="button">给子页面发消息</button>
<iframe src="http://127.0.0.1:5500/iframe/children.html" name="childrenName"></iframe>
<script>
    var button = document.getElementById('button');
    button.onclick=function()
        if(childrenName.document.readyState=="complete")
            childrenName.window.fnChild('hello啊,在吗');  // 向子页面问好
        
    
</script>
 
 
 
// 子页面
<div id="hhhh">我是子页面</div>
<script>
    function fnChild (arg) 
        console.log(arg);  // 成功打印出‘hello啊,在吗’
    
</script>

当然以上的前提的话是iframe中的内容已经加载完毕,否则是会报错的;
判断iframe 加载是否完成有2种方法
1,childrenName.document.readyState == 'complete’来判断;
2,childrenName.onload = function() 使用onload 回调函数,把调用的方法都写在这个回调函数里面

子调父

// 父页面
function receive(arg) 
    console.log(arg)

 
// 子页面
parent.window.receive('不在');

当然我们也会牵扯到父子元素的引用:

父页面获取子页面元素操作

我们还是使用上面的html

//原生js 获取子页面window对象
1、var childWindow = document.getElementById("hhhh").contentWindow;
2、var childWindow = document.getElementsByTagName('div')[0].contentWindow;
// 其实就是普通的获取方法,只是后面多了一个contentWindow;
// jquery
var childWindow = $('#hhhh').contentWindow;
 
// 获取子页面的document对象 (假设已经通过上面的方法得到了window对象)
var frameDoc = childWindow.document;
var frameBody = frameDoc.body;
// jquery 也是跟上面的一样
var frameDoc = $(childWindow.document);
 
// 原生获取元素
childWindow.document.getElementById('a') // 上面都已经拿到了子页面的window对象,所以获取子页面的元素也就只需要想普通操作那样获取就好
childWindow.document.getElementById('a').style.color='red' // 改个颜色
 
// jq拿子页面元素
$('#f').contents().find('#a'); // $('#f').contents 这相当于拿到了iframe 里面所有的dom;

子页面访问父页面元素

// 原生js
window.parent.document.getElementById('a'); // window.parent获取到父页面的window对象,那么接可以使用一般操作获取元素
window.parent.document.getElementById('a').style.color="red";// dom操作
// jquery
$("#a",parent.document); // $(父页面元素选择器, parent.document);
$("#a",parent.document).css('border','1px solid red');

三. iframe跨域通信

父传子

// 父页面
<iframe id="iframe" src="http://test.com/childrenCrossDomain.html" name="childrenName"></iframe>
   
<script>
      let iframe = document.getElementById('iframe');
      function sendToChildren()
          iframe.contentWindow.postMessage('hello啊,在吗', 'http://test.com')
      
</script>
 
// 子页面
<script>
      window.addEventListener("message", function(e)
          console.log(e.data)
      );
</script>

子传父

// 父页面
window.addEventListener('message',function(e)
    console.log(e.data)//不在
)
 
// 子页面
window.parent.postMessage('不在', 'http://父域名');
 window.parent.postMessage('fail', '*');

父子页面元素操作

跨域情况下是不能直接获取子页面元素的,但如果是同一主域的跨域,如:aaa.test.com和bbb.test.com,可以采用如下方法:
实现的关键点,是在父、子页面都加入一条js语句:

<script language="javascript">
  document.domain = "test.com";
</script>

父页面代码:

<body>
    <div id="parentPage">aaa</div>
</body>
<script type="text/javascript">
  document.domain = "test.com";
  setTimeout(function()
  //父页面获得子页面某元素的html内容
       console.log(document.getElementById("iframe1").contentWindow.document.getElementById("sonPage").innerHTML);
  ,3000);
</script>
<iframe id="iframe1" name="iframe1" style="width:0px;height:0px" src="http://bbb.test.com/test.html">

子页面代码:

<body>
   <div id="sonPage">bbb</div>
</body>
<script type="text/javascript" >
    document.domain = "test.com";
    //子页面赋html内容给父页面某元素
    window.parent.document.getElementById("parentPage").innerHTML = "123";
</script>

以上是关于Iframe通信的主要内容,如果未能解决你的问题,请参考以下文章

附实例!实现iframe父窗体与子窗体的通信

iframe跨域通信方案

为啥前端尽量少用iframe

iframe页面间通信

为啥前端尽量少用iframe

Web前端之iframe详解