• 嵌套iframe下父子页面之间的同域与跨域通信


    同域:

    获取另外一个iframe:window.parent.frames[ "I3" ].document.body;

    iframe框架中的页面与主页面之间的通信方式根据iframe中src属性是同域链接还是跨域链接,有明显不同的通信方式,同域下的数据交换和DOM元素互访就简单的多了,而跨域的则需要一些巧妙的方式来实现通信。

    一、同域下父子页面的通信

    父页面 parent.html

    1. <html>
    2. <head>
    3. <script type="text/javascript">
    4. function say() {
    5. alert("parent.html------>I'm at parent.html");
    6. }
    7. function callChild()
    8. {
    9. //document.frames["myFrame"].window.say();//只适用于ie浏览器
    10. myFrame.window.say();
    11. myFrame.window.document.getElementByIdx_x_x("button").value="我变了";
    12. }
    13. </script>
    14. </head>
    15. <body>
    16. <input type=button value="调用child.html中的函数say()" onclick="callChild()">
    17. <iframe name="myFrame" src="child.html"></iframe>
    18. </body>
    19. </html>

    子页面 child.html

    1. <html>
    2. <head>
    3. <script type="text/javascript">
    4. function say()
    5. {
    6. alert("child.html--->I'm at child.html");
    7. }
    8. function callParent() {
    9. parent.say();
    10. parent.window.document.getElementsByName("myFrame")[0].style.height="100px";
    11. }
    12. </script>
    13. </head>
    14. <body>
    15. <input id="button" type=button value="调用parent.html中的say()函数" onclick="callParent()">
    16. </body>
    17. </html>

    方法调用

    如上面示例所示父页面调用子页面的方法可通过:FrameName.window.childMethod();(这种方式兼容各种浏览器)
    子页面调用父页面的方法:parent.window.parentMethod();

    DOM元素访问

    根据FrameName.window得到了子窗口对象之后,再访问其中的DOM元素就跟访问同一页面中的DOM元素没区别了都可以通过 document.getElementByIdx_x_x(),document.getElementsByName()[index]。 如:parent.window.document.getElementsByName("myFrame") [0],myFrame.window.document.getElementByIdx_x_x("button")其中的window都是可以省略的。

    注意事项

    要确保在Iframe加载完成后再进行操作,如果Iframe还未加载完成就开始调用里面的方法或变量,无疑会产生错误。判断Iframe是否加载完毕有两种方法:
    1.在Iframe上用onload事件;
    2.用document.readyState=="complete"来判断

    二、跨域父子页面通信方法

    如果iframe所链接的是外部页面,因为安全机制则不能使用同域名下的通信方式了。在《SNS平台与第三方APP的JS通信实现》中提供易于且具有通用性的通信方式。

    父页面向子页面传递数据

    实现的技巧就是利用 location 对象的 hash 值,通过它传递通信数据,我们只需要在父页面设置 iframe的 src 后面多加个#data 字符串(data就是你要传递的数据),然后在 子页面 中通过某种方式能即时的获取到这儿 data 就可以了,其实常用的一种方式就是:
    1. 在 子页面 中通过 setInterval 方法设置定时器, 监听 location.href 的变化即可获得上面的 data 信息
    2. 然后 子页面 就能根据这个 data 信息进行相应的逻辑处理。

    先看一下我们遇到了什么问题?

    在我们的白社会里,需要嵌入第三方应用,而嵌入的方式是使用 iframe,为了页面美观,这里就有一个最简单的需求:iframe 的高度需要跟随其本身内容的变化而实时变化,这就要求主页面根据 iframe 的内容实时的去设置其样式 height 值,但是因为第三方应用和白社会不属于同一个域,所以给实现带来了一点小小的麻烦,所以才有以下的一些讨论…

    仔细分析一下问题的实质是什么呢?

    其实这里需要解决的是,在一个页面 A 中嵌入一个iframe B,A 和 B 不属于同一个域,但是 A 和 B 需要进行一些必要的通信,传递少量的数据信息,所以问题的实质就是主页面与跨域 iframe 之间怎么通信,也就是怎么传递数据信息

    下面就针对两种不同的需求,总结一些比较简单,常用和稳定的解决方案。

    • 主页面A 怎么向 iframe B 传递数据
    • iframe B 怎么向 主页面A 传递数据

    需求一:主页面A 怎么向 iframe B 传递数据呢?

    这种方式,是主页面需要给 iframe B 传递数据,然后 iframe B 获得到数据后进行特定的处理

    实现方式

    实现的技巧就是利用 location 对象的 hash 值,通过它传递通信数据,我们只需要在主页面A中设置 iframe B 的 src 后面多加个 #data 字符串(data就是你要传递的数据),如下图所示:

    主页面A 向 iframe B 传递数据

    然后在 iframe B 中通过某种方式能即时的获取到这儿 data 就可以了,其实常用的一种方式就是:

    • 1. 在 iframe B 中通过 setInterval 方法设置定时器, 监听 location.href 的变化即可获得上面的 data 信息
    • 2. 然后 iframe B 就能根据这个 data 信息进行相应的逻辑处理

    需求二:iframe B 怎么向 主页面A 传递数据呢?

    这种方式,是 iframe B 需要给主页面传递数据,然后主页面根据获得到数据后进行特定的处理

    实现方式

    实现的技巧就是利用一个代理 IframeC,它嵌入到 iframe B 中,并且和主页面A必须保持是同域,然后我们通过它充分利用上面第一种通信方式的实现原理就能把 iframe B 的数据传递给 iframeC,接下来的问题就是怎么让iframeC把数据传递给主页面A ,如下图所示:

    iframe B  向 主页面A 传递数据

    因为,iframeC 和主页面是同域的,所以它们之间传递数据就变得简单多了,我们这里的方式就是使用一个经常使用的属性 window.top (也可以使用window.parent.parent),它返回对载入浏览器得最顶层 window 对象的引用,这样我们就能直接条用主页面A中方法啦,哈哈哈,简单吧。

    到此,我们做个简单分析总结

    当然还有其他一些方式,也都测试过,不是浏览器兼容性不好,就是实现起来复杂,通过以上方式就能很方便的在跨域的 iframe 和主页面之间传递数据了,当然也就能解决上面提到的设置 iframe 高度的问题了,但是这种实现方式的前提也是最大的缺点就是 iframe 中的内容必须是我们可控的,但是至少我们这种实现方式是建立在浏览器的安全规则之上的,没有破坏应用本身的安全性。

    进一步叨叨,实现时需要考虑的一些细节

    上面的分析,其实只是一个简单的原理,在白社会里,虽然我们目前的需求还仅仅是实现第三方 iframe 形式的 App 的高度自适应,但是我们在实现的时候尽量考虑到了易用,可扩展性和可维护性,比如:

    • 让第三方 App 只需加载一个我们提供的JS种子文件就能很方便的使用我们为其提供的各种工具
    • 上面的各种工具,我们采用包的形式进行组织,最大化的实现按需加载
    • 第一条中的JS种子文件只提供基础的方法实现,并且把最常用的工具包放在里面,比如高度自适应
    • 通过种子文件,我们还提供给第三方 App 一些常用的JS工具包,而且直接使用的类似YUI3模块的动态加载机制就可使用指定的工具包
    • 对第三方 App 和 主页面传递的数据进行分类(自我调用,登录验证,传递数据等等)
    • 传递的数据使用满足特定规范的JSON格式,并通过统一的服务出口发出去,主页面提供一个统一服务接口解析数据,并根据规范调用相应的方法
    • 还有,就是版本控制的问题,为了尽量减少给第三方App带来影响,以上所有这些JS文件的版本都是采用向后兼容的策略,小版本使用服务器设置SQUID缓存特定频率的失效时间实现,大版本更新根据用户自己的需求手动更改
    • 当然,以上可能不是最优的解决方案,只是希望能给你一些帮助和引导,我们也在逐步的改进我们的一些实现方式,比如版本控制这块儿,我们也有一些问题需要解决

    回过头来,我们再看点儿具体的代码

    主页面A的源码

    1. /*主页面A*/
      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
      <title>主页面A</title>
      <script type="text/javascript">
      function init(){
          document.domain = 'bai.sohu.com';
          alert('我是主框架,嵌入了第三方应用IframeB,下面开始加载应用');
          var iframeTag = document.getElementById('frameB'),
               iframeSrc = 'http://test.com/iframePage.html';
         
          iframeTag.src = iframeSrc;
          iframeTag.style.display = 'block';
      };
       
      function callback(h){
          var iframeB = document.getElementById('frameB');
          alert('IframeC调用我(主框架)接口,把IframeB的高度传给我,具体值是:' + h);
          iframeB.style.height= h + 10 + 'px';
          iframeB.src += '#'+ h
      };
      </script>
      </head>
      <body onload="init();">
          <p>我是主页框架,我的域是:bai.sohu.com</p>
          <iframe id="frameB" style="display:none;"></iframe>
      </body>
      </html>

    iframeB(iframePage.html)的源码

    1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
      <title>iframeB</title>
      </head>
      <body onload="init();">
          <p style="height:500px;">我是三方应用,我的域是:test.com</p>
          <iframe id="frameC" style="height:1px;1px;display:none;"></iframe>
      </body>
      </html>
      <script type="text/javascript">
      function init(){
          alert('我是第三方App,下面开始创建和主框架同域的通信通道IframeC,并设置它的src,用#号传递高度值');
         
                   var iframeTag = document.getElementById('frameC'),
                iframeSrc = 'http://bai.sohu.com/iframePageC.html#',
                         pageHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
         
          iframeTag.src = iframeSrc + pageHeight;
          iframeTag.style.display = 'block';
         
          window.setTimeout(function(){
              alert('主页面设置我(IframeB)的src,通过Hash(#)给我传递它收到的高度:' + location.hash);
          },2000);
      };
      </script>

    iframeC(iframePageC.html)的源码

    1. <script type="text/javascript">
          document.domain = 'bai.sohu.com';
          alert('我(IframeC)收到iframeB通过参数(#)给我传递高度值,我现在调用主页面方法去设置IframeB的高度');
          top.callback(window.location.href.split('#')[1]);
      </script>


     

    完结了

  • 相关阅读:
    Delphi制作带图标的弹出式选单 DELPHI
    在DBGrid中实现Copy、Paste功能 DELPHI
    使用stringgrid的例子 DELPHI
    取得某一dll所有输出函数名 DELPHI
    HDOJ 2512 一卡通大冒险
    POJ 2533 Longest Ordered Subsequence
    UVA 10795 A Different Task
    HDOJ 1505 City Game
    HDOJ 1864 最大报销额
    HDOJ 1421 搬寝室
  • 原文地址:https://www.cnblogs.com/zhuyang/p/2768149.html
Copyright © 2020-2023  润新知