跨域

方法 1:document.domain

修改 document.domain 来跨子域,当前页面与 iframe 页面都设置相同的 domain

<!-- 父页面 -->
<iframe id="iframe" src="son.html" onload="test()"></iframe>
// 父页面
document.domain = 'aaa';
function test() {
    let win = document.getElementById('iframe').contentWindow;
    win.a();
}

// 子页面
document.domain = 'aaa';
function a() {
    console.log('子页面');
}

方法 2:window.name

window.name 大小可达 2m

window 对象有个 name 属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个 window.name 的,每个页面对 window.name 都有读写的权限,window.name 是持久存在一个窗口载入过的所有页面中的,并不会因 新页面的载入而进行重置。

即我们在 http://www.baidu.com 页面的控制台设置window.name="name=123",然后我们在控制台修改 window.location.href="http://www.youku.com",跳转页面后我们在控制台打印 window.name,我们发现结果为我们刚才设置的 name=123,利用此原理我们可以利用 window.name 来传递信息

获取流程如下

  • 子页面先设置好要获取的数据
  • iframe 嵌入子页面,此时 iframe 可以获取到子页面的数据
  • 父页面和 iframe 不同源无法直接获取,因此将 iframe 跳转到与父页面同源的页面(注意 iframe 初次加载与跳转页面都会执行 onload 事件,因此下面代码中的 loadData 方法是可以正常运行的)
  • 根据 window.name 的特性,页面跳转不会改变其值,因此跳转后父页面即可通过 iframe 获取数据了
// 父页面
function test() {
    let boo = false;
    let iframe = document.createElement('iframe');
    let loadData = function () {
        if (boo) {
            // 获取子页面 window.name,即要获取的数据
            let data = iframe.contentWindow.name;
            // 获取到数据后将隐藏的iframe去除
            iframe.contentWindow.document.write('');
            iframe.contentWindow.close();
            document.body.removeChild(iframe);
        } else {
            boo = true;
            // 随意写,与当前页面同源即可
            iframe.contentWindow.location.href = 'about:blank';
        }
    };
    iframe.src = 'son.html';
    if (iframe.attachEvent) {
        iframe.attachEvent('onload', loadData);
    } else {
        iframe.onload = loadData;
    }
    document.body.appendChild(iframe);
}
test();

// 子页面
window.name = 'son';

方法 3:location.hash

子页面修改父页面 src 的 hash 值,通过该属性传递数据,但长度有限

  • 首先准备 3 个页面:a、b、c,a、c 同源,b 不同源
  • a 嵌入 b,b 嵌入 c,a 页面定义一个方法供 c 调用,用以返回数据
  • b 监听自身 hash 改变,然后更改 c 的 hash
  • c 监听自身 hash 改变,然后调用 a 定义的方法,传回数据
<!-- a 页面 -->
<iframe id="iframe" src="b.html"></iframe>

<!-- b 页面 -->
<iframe id="iframe" src="c.html"></iframe>
// a 页面
let iframe = document.getElementById('iframe');
setTimeout(() => {
    iframe.src = iframe.src + '#user=admin';
}, 1000);
function onCallback(res) {
    console.log('data from c.html -> ' + res);
}

// b 页面
let iframe = document.getElementById('iframe');
window.onhashchange = function () {
    iframe.src = iframe.src + location.hash;
};

// c 页面
window.onhashchange = function () {
    window.parent.parent.onCallback(
        'hello:' + location.hash.replace('#user=', '')
    );
};

方法 4:postMessage

// 父页面
window.onload = function () {
    let iframe = document.getElementById('iframe');
    iframe.contentWindow.postMessage('aaa', 'url');
    window.addEventListener('message', receiveMessage, false);
    function receiveMessage(e) {
        console.log(e.data);
    }
};

// 子页面
window.addEventListener('message', receiveMessage, false);
function receiveMessage(e) {
    console.log(e.data);
    e.source.postMessage({ a: 1 }, e.origin);
}

方法 5:jsonp

利用 script 文件不跨域的特性

其中 callback 是服务端定义的方法,名称随意,双方确定好即可

<!-- 父页面 -->
<script>
    function callbackFn(data) {
        console.log(data);
    }
</script>
<script src="jsonp.js?callback=callbackFn"></script>
// jsonp.js

callbackFn({
    code: 1,
    data: [{ id: 1, name: 2 }],
});
Last Updated:
Contributors: zhangfei