【JavsScript】父子页面之间跨域通信的方法
由于同源策略的限制,JavaScript跨域的问题,一直是一个比较棘手的问题,为了解决页面之间的跨域通信,大家煞费苦心,研究了各种跨域方案。之前也有小网同学分享过一篇“跨域,不再纠结” 开始照着尝试时还是有些不够明白的地方,深入了解之后,这里给大家补充一点更具体的做法。
先来看看哪些情况下才存在跨域的问题:

其中编号6、7两种情况同属于主域名相同的情况,可以设置domain来解决问题,今天就不讨论这种情况了。 对于其他跨域通信的问题,我想又可以分成两类:
其一(第一种情况)是a.com下面的a.js试图请求b.com下某个接口时产生的跨域问题。
其二(第二种情况)是当a.com与b.com下面的页面成父子页面关系时试图互相通信时产生的跨域问题,典型的应用场景如a.com/a.html使用iframe内嵌了b.com/b.html,大家都知道a.html内的js脚本试图访问b.html时是会被拒绝的,反之亦然。
第一种情况,目前主流的方案是JSONP,高版本浏览器支持html5的话,还可以使用XHR2支持跨域通信的新特性。
第二种情况,目前主要是通过代理页面或者使用postMessageAPI来做,这也是今天要讨论的话题。第二种情况,有这样一些类似的案例:a.com/a.html使用iframe内嵌了b.com/b.html,现在希望iframe的高度能自动适应b.html的高度,使iframe不要出现滚动条。我们都知道跨域了,a.html是没办法直接读取到b.html的高度的,b.html也没办法把自己的高度告诉a.html。 直接说可以用代理页面的方法搞定这个问题吧,但是怎么代理法,先来看下面这张图:

b.html与a.html是不能直接通信的。我们可以在b.html下面再iframe内嵌一个proxy.html页面,因为这个页面是放在a.com下面的,与a.html同域,所以它其实是可以和a.html直接通信的,假如a.html里面有定义一个方法_callback,在proxy.html可以直接top._callback()调用它。但是b.html本身和proxy.html也是不能直接通信的,所谓代理页面的桥梁作用怎么实现呢? b.html内嵌proxy.html是通过一段类似下面这样的代码: <iframe id=”proxy” src=”a.com/proxy.html” name=”proxy” frameborder=”0″ width=”0″ height=”0″></iframe> 这个iframe的src属性b.html是有权限控制的。如果它把src设置成a.com/proxy.html?args=XXX,也就是给url加一个查询字符串,proxy.html内的js是可以读取到的。对的,这个url的查询字符串就是b.html和proxy.html之间通信的桥梁,美中不足的是每次通信都要重写一次url造成一次网络请求,这有时会对服务器及页面的运行效率产生很大的影响。同时由于参数是通过url来传递的,会有长度和数据类型的限制,搜集的资料显示:
- IE浏览器对URL的长度现限制为2048字节。
- 360极速浏览器对URL的长度限制为2118字节。
- Firefox(Browser)对URL的长度限制为65536字节。
- Safari(Browser)对URL的长度限制为80000字节。
- Opera(Browser)对URL的长度限制为190000字节。
- Google(chrome)对URL的长度限制为8182字节。
上面的方法,通过迂回战术实现了b.html跟a.html通信,但是倒过来,a.html怎么跟b.html通信呢?嵌入在b.html里面的proxy.html可以用top快速的联系上a.html,但是要想让a.html找到proxy.html就不容易了,夹在中间的 b.html生生把它们分开了,a.html没法让b.html去找到proxy.html然后返回给它。只能采用更迂回的战术了。 顺着前面b.html到a.html的通信过程,逆向的想一下,虽然a.html没有办法主动找到proxy.html,但是proxy.html可以反过来告诉a.html它在哪里: 在proxy.html加这么一段脚本:

也就是必须由proxy.html先主动发送一个消息给a.html,a.html得到proxy.html页面window的引用,就可以反过来向它发送请求了。 现在a.html可以把消息发给proxy.html了,但是proxy.html怎么把消息转送到b.html?似乎这才是难点,因为它们之间才真正有着“跨域”这一道鸿沟。 这回我们不再用前面那个iframe内嵌代理页面的方法再在proxy.html内嵌一个b.com下面的代理页面了,这样实在会给人感觉嵌的太深了,四层。但是为了跨越这道鸿沟,b.com下面也加一个代理页面是免不的。不过现在我们要利用一下window.name。window.name有一个特性,就是页面在同一个浏览器窗口(标签页)中跳转时,它一直存在而且值不会改变。比如我们在a.html中设置了window.name=”a”,然后location.href=”http://b.com/b.html”跳转后,b.html可以读取window.name的值为”a”;而且window.name的值长度一般可以到达2M,ie和firefox甚至可以达到32M,这样的存储容量,足够利用起来做跨域的数据传递了。好吧,我们现在要做的就是当proxy.html拿到a.html发送过来的数据后把这个数据写入window.name中,然后跳转到b.com下面的代理页面,我们这里假设是bproxy.html。bproxy.html读取到window.name值后,通知给它父页面b.html就简单了。我们再来看这个过程可以用图大概示意一下:

图例中绿色的双向箭头表示可以通信,橙色的双向箭头表示不能直接通信。 最后我们简单看一下双向通信的实测效果:

b.html每次加载的时候都先给a.html发一个”连接请求”,让a.html可以找到proxy.html。所以页面第一次加载的时候会产生三个请求:

每次b.html向a.html发送消息的时候会产生一个请求:

每次a.html向b.html发送消息的时候会产生两个请求,其中一个是a.com/proxy.html向b.com/bproxy.html跳转产生的,另一个是b.html重新向a.html发起“连接请求”时产生的:

最后简单看一下实测的几个测试页面代码:
代码片段一,a.com/a.html:

代码片段二,a.com/proxy.html:

代码片段三,b.com/b.html:

代码片段四,b.com/bproxy.html:

好吧,现在我必须把话锋调转一下了。前面讲的这么多,也只是抛出来一些之前我们可能会采用的跨域通信方法,事实上代理页面、url传参数和window.name、甚至还有一些利用url的hash值的跨域传值方法,都能百度到不少相关资料。但它们都逃不开代理页面,也就不可避免的要产生网络请求,而事实上这并不是我们的本意,我们原本希望它们能够直接在客户端通信,避免不必要的网络请求开销——这些开销,在访问量超大的站点可能会对服务器产生相当大的压力。那么,有没有更完美一点的替代方案呢? 必须给大家推荐postMessage。postMessage 正是为了满足一些合理的、不同站点之间的内容能在浏览器端进行交互的需求而设计的。利用postMessage API实现跨域通信非常简单,我们直接看一下实例的代码:
代码片段五,A.com/a.html:


代码片段六,b.com/b.html:
代码的关键是message事件是一个拥有data(数据)和origin(来源)属性的DOM事件。data属性是发送的实际数据,origin属性是发送来源。Origin属性很关键,有了这个属性,接收方可以轻易的忽略掉来自不可信源的消息,也就能有效避免跨域通信这个开口给我们的源安全带来的隐患。接口很强大,所以代码很简单。我们可以抓包看一下,这个通信过程完全是在浏览器端的,没有产生任何的网络请求。同时这个接口目前已经得到了绝大多数浏览器的支持,包括IE8及以上版本,参见下面的图表:

但是为了覆盖ie6等低版本浏览器,我们完整的方案里面还是要包含一下兼容代码,就是最开始介绍的代理页面的方法了,但必须是以postMessage为主,这样即便最后会有某些浏览器因为这种通信产生一些网络请求,比例也是非常低的了。
http://kb.cnblogs.com/page/198884/
【JavsScript】父子页面之间跨域通信的方法的更多相关文章
- 【JavaScript】父子页面之间跨域通信的方法
由于同源策略的限制,JavaScript跨域的问题,一直是一个比较棘手的问题,为了解决页面之间的跨域通信,大家煞费苦心,研究了各种跨域方案.之前也有小网同学分享过一篇“跨域,不再纠结” 开始照着尝试时 ...
- 使用HTML5 的跨域通信机制进行数据同步
离线应用系统的设计目标就是在网络离线情况下依然可以操作我们的应用系统,并在网络畅通的情况下与服务器进行数据交互. 所以离线应用系统最终会做成类似C/S架构的客户端应用程序.这边基于Chrome或者 S ...
- iframe父子页面之间相互调用元素和函数
<!doctype html> <html> <head> <meta http-equiv="Content-Type" content ...
- JavaScript 跨域:window.postMessage 实现跨域通信
JavaScript 跨域方式实现方式有很多,之前,一篇文章中提到了 JSONP 形式实现跨域.本文将介绍 HTML5 新增的 api 实现跨域:window.postMessage . 1 othe ...
- 使用 JSONP 实现跨域通信
简介 Asynchronous JavaScript and XML (Ajax) 是驱动新一代 Web 站点(流行术语为 Web 2.0 站点)的关键技术.Ajax 允许在不干扰 Web 应用程序的 ...
- iframe跨域通信方案
概述 JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象.但在安全限制的同时也给注入iframe或是ajax应用上带来了不少麻烦.这里把涉及到跨域的一些问题简单地整理一下: 首先什么 ...
- JavaScript系列----AJAX机制详解以及跨域通信
1.Ajax 1.1.Ajax简介 Ajax简介这一部分我们主要是谈一下ajax的起源,ajax是什么?因为这些是跟技术无关的.所以,大多细节都是一笔带过. Ajax的起源? Ajax一词源于2005 ...
- web跨域通信问题解决
Web页面的跨域问题产生原因是企图使用JS脚本读写不同域的JS作用域.问题根源来自JavaScript的同源策略:出于安全考虑,Javascript限制来自不同源的web页面JS脚本之间进行交互.否则 ...
- html5 postMessage解决iframe跨协议跨域通信问题
a.html有个iframe载入b.com/login.html,当login完成时通知a.html页面登录完成并传递UserName 1.a.html 监听消息 window.addEventLis ...
随机推荐
- awk与sed简明教程
看到大牛写的关于awk和sed的简明教程,写得很好,为了尊重作者,就不全文转载了,这里标记下链接,方便以后查阅. awk简明教程:http://coolshell.cn/articles/9070.h ...
- Python【基础第四篇】
一.迭代器(iterator) 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退. ...
- 代理模式(proxy)
1.代理模式 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问. 在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 代理模式一般 ...
- openstack-dbs
真正的服务器派生出线程 和子进程处理多个连接当允许客户端加入聊天室,他发送的任何一条文本都将广播给聊天室中的每个用户,除非文本是服务器CLI当广播一条消息,消息前面将加上发送者的昵称 以尖括号括住昵称 ...
- hive权威安装出现的不解错误!(完美解决)两种方法都可以
以下两种方法都可以,推荐用方法一! 方法一: 步骤一: yum -y install mysql-server 步骤二:service mysqld start 步骤三:mysql -u root - ...
- 操作失败,没有该服务权限![ 机构号:99 ,用户ID:50000009 ,服务号:0101030112 ]
操作失败,没有该服务权限![ 机构号:99 ,用户ID:50000009 ,服务号:0101030112 ] 此时我们可以把代码 SERVICE_NO 改成 10个0 即 0000000000 ,就可 ...
- 应用TcpListener实现的socket服务器端
前言 项目中要实现一个简单的socket服务器端,采用了TcpListener这个类.除了基本的功能之外,有几处需要注意的点. 要能同时接收多个客户端的连接,当然,不需要几千个那么多. 要能探测到客户 ...
- Android SDK Manager更新不了的解决办法
android SDK Manager更新不了,出现错误提示:"Failed to fetch URL..."! 可以用以下办法解决: 使用SDK Manager更新时出现问题 F ...
- VS2008注册码
PYHYP-WXB3B-B2CCM-V9DX9-VDY8T 如果下载的是90天的试用版,下载下来以后把ISO里面的Setup\setup.sdb文件用记事本打开,把其中的[Product Key]下面 ...
- eclipse 最有用的10个快捷键
1Eclipse中10个最有用的快捷键组合 一个Eclipse骨灰级开发者总结了他认为最有用但又不太为人所知的快捷键组合.通过这些组合可以更加容易的浏览源代码,使得整体的开发效率和质量得到提升. ...