跨域的种类
一般有两种形式的跨域问题:
①使用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、不跨域:
父:
员工们:
document.getElementById("test").onclick = function(){ alert(document.getElementById("iframe").contentWindow.document.getElementById("message").value); }
子:
JSONP方式
document.getElementById("test").onclick = function(){ alert(parent.document.getElementById("message").value); }
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)
A
员工们:
document.domain = "jiaju.com"; document.getElementByI d("test").onclick = function(){alert(document.getElementByI d("iframe").contentWindow.document.getElementByI d("message").value);}
b.html ((http://b.xxx.com/com/js/crossdomain/demo/b.htm ))
JSONP方式
document.domain = "jiaju.com";document.getElementByI d("test").onclick = function(){alert(parent.document.getElementByI d("message").value);}
两个域都设置:document.domain=‘jiaju.com’
3、主域不同
1)location.hash
location.hash
原理:
1、动态改变location.hash
,iframe
不会重载
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//子页面=》父页面:要知道iframeidvar 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":try{top.window.document.getElementByI d(data["iframeID"]).height = data["height"];}catch(e){}breakcase "width":try{top.window.document.getElementByI d(data["iframeID"]).width = data["width"];}catch(e){}breakcase "callback":try{top.window[data["fn"]].call(document,data);}catch(e){}breakdefault:}
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代码:
// 1)触发父页面定义的方法window.SDK.sayHi({msg: 'hi'});// 2)向父页面请求获取数据 unamevar uname = '';window.SDK.getUname();setTimeout(function(){uname = window.SDK.uname;//doSomething(uname);}, 200);// 备注:发送请求后,需要延时接收返回的数据
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}}});//写入windowwindow.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}}});})();