有机会在项目中有要通过 iframe 进行跨域通信的需求,然后自己查找资料并简单分析了下通过 iframe 进行跨域的方式。主要通过标准浏览器的 postMessage() 方法和 IE 系列的 window.name 方法。

先说说应用场景吧,就是在页面内嵌的 iframe 之间进行通信,但同源策略限制,所以不同源之间的操作有诸多限制,所以就有了跨域的通信解决方案。

postMessage() 方法

postMessage 是现代浏览器原生支持的一个 window 对象上面的一个方法。这里的 window 对象指的是 frame 的 contentWindowwindowtopparent 等 window 对象,比如 a 和 b 都是 window 对象,那么在 b 中使用 a.postMessage( data, origin ); 就能在调用这个代码的地方向 a 发送数据了,参数 data 就是指代发送的数据,origin是对 a 进行限制,具体用法如下:

orogin 是一个必要的参数,可以的取值有 "*""/",还有类似于http://alphatr.com:8080/ 这样的带有主机名,协议,端口的 URL,其中端口默认是 80,可以省略。

postMessage 可以使用 onmessage 方法捕捉到别的发过来的消息,如下

onmessage = function(e) {
    e.data // 指代的上面 postMessage 发送过来的 data
    e.origin // 和上面的 origin 不同,指代的是来源的 URL ,这里就是 b;
    e.timeStmap // 时间戳
}

如上,也可以使用 window.addEventListener('message', function(e){}); 或者 window.attachEvent('onmessage', function(e){}); 这两种方法。

在标准的浏览器上面可以用 postMessage 进行通信,那么在不支持这种方法的低版本 IE 上面就不行了,低版本的 IE 可以使用 window 对象的 name 属性来进行跨域的数据通信。

window.name 在低版本 IE 下面有跨域可以在 iframe 之间互相读取而不受限,所以在低版本 IE 下就能通过 互相重写,读取而进行数据的相互交换,但有个问题就是只能是字符串类型的,可以通过传输 json 对象数据转化来的字符串。

下面就是我自己通过参考一些资料,基于 Jquery 封装的一个兼容版本的跨域 iframe 通信方案

(function() {
    var util = {
        mix: $.extend,
        stringify: JSON.stringify,
        parse: JSON.parse
    }; // 基础的一些方法
    var usePM = ( typeof window.postMessage !== 'undefined'); // 是否支持 postMessage
    var PM = function( win ) {
        this._win = win;
        this._event = function(){};
        this._initialize();
    };
    util.mix( PM.prototype, {
        _initialize: function() { // 初始化
            var me = this;
            if ( usePM ) {
                if( window.addEventListener ) {
                    window.addEventListener('message', function(e){
                    me._event( util.parse(e.data));
                });
                } else {
                    window.attachEvent('onmessage', function(e){
                        me._event( util.parse(e.data));
                    });
                }
            } else {
                var lastName = window.name;
                setInterval( function() {
                    if( window.name != lastName && window.name != '' ) {
                        lastName = window.name;
                        me._event( util.parse(lastName));
                    }
                }, 50);
            }
        },
        onmessage: function(callback) { // 添加 onmessage 方法
            this._event = callback;
        },
        send: function( data, origin ) { // send 方法
            var wl = window.location;
            var sendOrigin = wl.href.substr( 0, wl.href.indexOf( wl.pathname ) + 1 );
            var sendData = {
                data: data,
                ts: (+(new Date)).toString(10),
                origin: sendOrigin
            }

            if( usePM ) {
                this._win.postMessage( util.stringify(sendData), origin || '*' );
            } else {
                this._win.name = util.stringify(sendData);
            }
        }
    });
    window.XPM = PM;
})();

因为低版本的 IE 不支持 JSON 对象,所以要想使用 JSON.stringify 和 JSON.parse 这两个方法的话需要引用 GitHub 库上的 json2.js 这个兼容文件。

然后,为了考虑低版本 IE 的问题,所以把 postMessage 的 data 又封装了一层,添加了 origin 和 ts(timeStmap) 时间戳,防止消息重复,并且能对消息来源做判断和限制。

使用方法
var f = window.parent;
var PM = new XPM(f);
PM.onmessage(function(e){
    alert(e.data);
    console.log(e);
});
$('button').bind('click',function(){
    var text = "this is a message!";
    PM.send(text);
});