第一部分 postMessage消息传递
CD.html
<div style="width:200px; float:left; margin-right:200px;border:solid 1px #333;">
<div id="color">Frame Color</div>
</div>
<div>
<iframe id="child" src="CD2.html"></iframe>
</div>
CD.js
// 通过window获取iframe
// window.frames是个伪数组,可以通过window.frames[index]或window.frames[name]来获取
window.function(){
window.frames[0].postMessage('getcolor','*');
window.addEventListener('message',function(e){
var color=e.data;
document.getElementById('color').style.backgroundColor=color;
},false);
}
CD2.js
window.addEventListener('message',function(e){
if(e.source!=window.parent) return;
var color=$('body').css('background-color');
window.parent.postMessage(color,'*');
},false);
详细解读:postMessage(data,origin)
一、 参数
1.data:要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。
2.origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为"*",这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
二、 接收消息
监听window的message事件就可以
window.addEventListener('message',function(e){
if(e.source!=window.parent) return;
var color=$('body').css('background-color');
window.parent.postMessage(color,'*');
},false);
e有几个重要属性:
data:顾名思义,是传递来的message(数据、消息)
source:发送消息的窗口对象
origin:发送消息窗口的源(协议+主机+端口号)
三、 使用场景
1.页面和其打开的新窗口的数据传递
2.多窗口之间消息传递
3.页面与嵌套的iframe消息传递
4.上面三个问题的跨域数据传递
tips:iframe引入的页面,可以相互通信;window.open打开的页面只能父页面向子页面发消息
第二部分:封装
/**
* 跨域js
*/
(function(window){
var CD = function(){
};
CD.prototype = {
version : "0.0.1",
component : {},
components : {},
interfaces : {},
/**
* 初使化
*/
init : function(){
this.component = {
type : this.isMaster()? "MASTER" : "SLAVE" ,
name : this.isMaster()? "MASTER" : window.name ,
url : window.location.host,
port : window.location.port || 80
};
if(!this.component.name){
throw new Error("Error iframe must has a name");
};
//this.components[this.component.name] = this.component;
this.register(this.component);
this.extends("register" ,function(data){
this.components[data.component.name] = data.component;
});
},
/**
* 是否是Master
* 如果窗口为顶层窗口则认为是Master
*/
isMaster :function(){
return window === window.top;
},
/**
* Salve将自身组件注册到MASTER端
*/
register : function(){
this.send("MASTER" , "register" ,{info:"i'm coming register!", component:this.component});
},
/**
* 扩展接口方法
* @param {String} name接口名称
* @param {Function} fun 接口方法
*/
extends : function(name,fun){
this.interfaces[name] = fun;
},
/**
* 打印跨域日志的方法
*
* @param {Object} mesg 要打印跨域消息的内容
*/
log : function( mesg){
if (!window.console || typeof window.console === 'undefined')
return;
window.console.log("["+new Date()+"]["+this.version+"]["+this.component.type+"]["+this.component.name+"]["+mesg.type+"][" + window.JSON.stringify(mesg)+"]");
},
/**
* 发送信息到其它组件 - html5原生态方法包装
*
* @param {Window} targetWindow目标系统window对象
* @param {String} targetUrl目标系统 URL
* @param {Object} mesg 对象
*/
postMessage : function(targetWindow, targetUrl, mesg){
this.log(mesg);
targetWindow.postMessage(JSON.stringify(mesg), targetUrl);
},
/**
* 发送消息方法
* @param {String} componentName组件名称
* @param {String} method接口名称
* @param {Object} data数据
* @param {Function} callback回调
*/
send : function(componentName,method,data,callback,type){
if(this.isMaster() && componentName ==="MASTER")
return;
var source = this.component.name;
var mesg = {
source : source,
target : componentName,
method : method,
data : data,
type : type || "REQUEST"
};
if(callback)
this.extends(method+"Callback" , callback) ;
var w = this.isMaster()? window.document[componentName] : window.top;
var host = this.isMaster()? this.components[componentName].host : "*";
//console.info(host);
this.postMessage(w, "*", mesg);
},
/**
* 处理接收到的其它系统的请求跨域请求
*
* @param {Event} event事件对象
*/
process : function(event) {
var mesg = JSON.parse(event.data);
//console.log(event);
this.log(mesg);
var interface1 = this.interfaces[mesg.method];
var result ;
if(interface1){
result = interface1.call(this,mesg.data);
}else{
throw new Error("["+this.component.name+"] not have interface:[" + mesg.method + "]");
}
if(result){
this.send(mesg.source, mesg.method + "Callback", result, null,"RETURN");
}
},
/**
* 绑定窗口事件,用于监听跨域事件
*/
listen : function(){
if (window.addEventListener) {// 非IE
window.addEventListener("message", function(event){
window.CD.process(event);
}, false);
} else {// IE
window.attachEvent("onmessage", function(event){
window.CD.process(event);
});
}
}
};
window.CD = new CD();
window.CD.init();
window.CD.listen();
})(window);
第三部分 使用
A. 父窗口iframe组件传递消息
CD.send('custom_frame_'+$scope.components[$scope.signIndex].code, 'SEND_PING_AN_YUN_INFO', args, function () {
});
参数1:目标iframe的名字
参数2:事件名(根据这个名字获取数据)
参数3:数据
回调函数:执行完执行
B. 对应iframe接收消息
CD.extends('SEND_PING_AN_YUN_INFO', function (result) {})
A1:iframe组件向父窗口发送消息
CD.send('MASTER','COME_FROM_COMPONENTS_EVENTS',{}, function(data) {
console.log('OPEN_PHONE_CURR_OPERATE:',data);
});
B1:父窗口接收对应组件消息
CD.extends('COME_FROM_COMPONENTS_EVENTS',function(args){
$scope.showComponent($scope.signIndex);
});