所谓js跨域问题,是指在一个域下的页面中通过js访问另一个不同域下的数据对象,出于安全性考虑,几乎所有浏览器都不允许这种跨域访问。现假设一个站下有两个域名A.com、B.com,下面结合具体实例,介绍在什么情况下会产生跨域问题以及如何解决它。
1、Ajax跨域请求
在A.com域下有一个页面,http://A.com/test.jsp,页面代码为:
- <script src="jquery-1.8.2.min.js" type="text/javascript"></script>
- <script type="text/javascript">
- $(function() {
- var ajax_url = "http://B.com/info.jsp";
- // var ajax_url = "http://A.com/info.jsp";
- $.get(ajax_url, function(data) {
- alert(data);
- });
- });
- </script>
如果以上页面通过ajax异步请求B.com下的info.jsp,就会产生跨域问题,而如果请求本域下的info.jsp,就会正常alert返回的信息。
2、页面中嵌入iframe
在A.com域下有一个页面,http://A.com/test.jsp,页面代码为:
- <body>
- <div id="div_info"></div>
- <iframe src="http://B.com/child.jsp"></iframe>
- </body>
我们看到child.jsp页面是在域B.com下的,
- <script src="jquery-1.8.2.min.js" type="text/javascript"></script>
- <script type="text/javascript">
- $(function() {
- $(window.parent.document).find("#div_info").html("Hello");
- });
- </script>
如上,在child.jsp中控制A.com域下test.jsp的页面元素div_info,即产生跨域问题,页面test.jsp中将不会显示字符串"Hello",但如果child.jsp也是在域A.com下,在test.jsp页面上就能看到"Hello"。
请注意,有一种情况经常会让大家误以为存在跨域问题,实则不然,如下:
在A.com域下有一个页面,http://A.com/test.jsp,页面代码为:
- <div id="div_info"></div>
- <script src="http://B.com/outer.js" type="text/javascript"></script>
如上,outer.js在B.com域下,代码为:
- // outer.js
- $(function() {
- $("#div_info").html("Hello");
- });
在这种情况下,并不会产生跨域问题,虽然test.jsp页面在域A.com下,其引用的outer.js是在域B.com下,页面test.jsp中的div仍然会显示"Hello"。(script的src不存在跨域问题)
如何解决跨域问题,共有三种解决方案。
1、在跨域的服务端使用Proxy
在A.com域下的页面http://A.com/test.jsp
- <script type="text/javascript" src="jquery-1.8.2.min.js" ></script>
- <script type="text/javascript" src="http://B.com/proxy.jsp"></script>
如上,test.jsp页面要获取B.com域下的信息,不能再使用Ajax方式异步获取,而是在<script>标签内加入一个服务端的代理,直接返回javascript语句,http://B.com/proxy.jsp的代码如下:
- <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
- <%
- out.print("alert('Hello');");
- %>
2、使用Jsonp
在A.com域下的页面http://A.com/test.jsp
- <script src="jquery-1.8.2.min.js" type="text/javascript"></script>
- <script type="text/javascript">
- $(function() {
- $.getJSON("http://B.com/jsonp.jsp?callback=?", function(data){
- alert(data);
- });
- });
- </script>
如上,test.jsp页面要获取B.com域下的信息,可以通过Jsonp方式获取http://B.com/jsonp.jsp中的内容,jsonp.jsp的代码如下:
- <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
- <%
- String callback = request.getParameter("callback");
- out.print(callback + "(\"Hello\")");
- %>
3、使用iframe解决js跨域问题
baidu salon分享会上黄方荣主讲的《WEB数据交互的艺术》中提到一个非常优雅绝妙的解决方案!话不多说,直接上解决方案原理图:
该图要解决的问题说明如下:
在AAA.com域名下的index.htm页面中内嵌了BBB.com域 名下的一个页面index.htm,正常情况下iframe内部的index.htm页面是无法访问父页面index.htm中的任何dom对象或者js 函数的,因为跨域,但我们经常又需要做一些参数回传的事情怎么办呢?以上的这种实现方式就很好的解决了这个问题;
解决方案的关键优雅之处在于:浏览器虽然会禁止js跨域访问页面中的对象,但对于iframe的层级关系引用并没有做限制,即parent仍然可用;该方案就是利用了2层内嵌 iframe、使用第二级iframe中的页面与parent.parent的页面是同域名的关系,从而避免跨域问题实现两个页面间相关数据的传递,本质上就是利用parent.parent实现对父父页面中js的回调!
举个实际的案例:
功能描述:
A域名下的页面index.htm中内嵌了一个iframe页 面,iframe内引用的是B域名的sub-index.htm页面,但是为了避免出现在index.htm页面中出现滚动条,我们需要明确知道sub- index.htm页面的高度和宽度,可是sub-index.htm的页面内容是不可控的,可能会根据不同用户页面大小会不一样;问题就是如何把 sub-index.htm页面的高度和宽度传递给index.htm页面?
具体操作流程:
1、在index.htm页面中声明一个js函数process(height, width);用来实现设置页面内iframe的高度和宽度;
2、 在sub-index.htm页面中再内嵌一个隐藏的iframe,iframe的src指向A域名下的页面ex.htm?height=xx& width=yy,该页面没有任何内容,只是用来传递sub-index.htm页面加载完之后的宽度和高度这两个数据的,页面内js拿到request 中的参数之后直接调用parent.parent.process(height, width);完成对父页面宽度和高度的设置。