这是我参与8月更文挑战的第19天,活动详情查看:8月更文挑战
前言
iframe平时用的不多,所以今天看到有位小伙伴说,他用的iframe出现跨域问题。 我听完有点不知所措,因为我们平时遇到的跨域问题一般是请求的跨域比较多。所以对于iframe的跨域了解的不多,今天就来好好复习一下。
iframe的跨域
iframe主要是用来嵌套第三方页面的,比如嵌入地图,新闻页面等等。只要把iframe的src的设置成第三方页面的页面地址,就可以显示了。
但是如果第三方页面跟主页面的域名不是同源的话,也就是不满足以下条件之一:
- 域名一致
- 协议一致
- 端口一致
就属于跨域了,这时候第三方页面可以正常显示,但是如果需要跟它通信就不可以了,比如说更改第三方页面的样式,调用它的方法,或者第三方页面调用主页面的方法或者修改样式。
下面我用代码来模拟下:
主页面a.html:
<div>
我是a页面
<iframe src="http://127.0.0.1:8081/b.html" frameborder="0" name="iframe"></iframe>
</div>
iframe页面b.html
<div>
我是b页面
</div>
<script>
console.log(window.parent.document) // 打印a页面的document
</script>
开2个终端运行,设置端口不一样,模拟跨域
可以看到
解决方法
postMessage方法
html5新增的跨页面通信方法
用法如下:
目标页面:
监听message事件
window.addEventListener('message', func)
触发页面:
targetWindow.postMessage(message, targetOrigin, [transfer]);
targetWindow对应的是目标页面的window对象
参数:
-
message
需要传递的数据
-
targetOrigin
目标页面的域名地址,等同于
location.origin -
transfer
可选参数,网上的解释不好理解,一般用不上(知道的朋友可以解释)
代码如下:
主页面a.html:
<div>
我是a页面
<iframe src="http://127.0.0.1:8081/b.html" frameborder="0"></iframe>
</div>
<script>
window.addEventListener('message', (res) => {
console.log('接受到b页面传来的数据', res)
})
window.onload = () => {
const iframe = document.querySelector('iframe')
// 需要等待iframe加载完再postMessage
iframe.contentWindow.postMessage({ text: '你好,我是页面a' }, 'http://127.0.0.1:8081')
}
</script>
</div>
iframe页面b.html:
<div>
我是b页面
</div>
<script>
window.parent.postMessage({ text: '你好,我是页面b' }, 'http://127.0.0.1:8080')
window.addEventListener('message', (res) => {
console.log('接受到a页面传来的数据', res)
})
</script>
双方都能接收和发送数据
结果如下:
设置document.domain
这个方法是用于域名的公共域的,通过设置这个document.domain,可以解决iframe的跨域问题。
不过这个只能解决主域一致,子域不同的跨域问题。
比如你主域是aaa.com, 子域名是test.aaa.com。这是有跨域问题的。
这时候我们可以设置document.domain等于aaa.com来解决,需要在主页面和iframe页面一起设置才生效。
a页面:
<div>
我是a页面
<iframe src="http://test.aaa.com/b.html" frameborder="0"></iframe>
</div>
<script>
document.domain = 'aaa.com'
</script>
b页面:
<div>
我是b页面
</div>
<script>
document.domain = 'aaa.com'
console.log(window.parent.document) // 打印需要设置之后
</script>
我是把我本地的host文件改了,127.0.0.1 指向了 aaa.com, 内网ip指向了test.aaa.com
打印成功
window.name + 中间页
在同一个窗口,
window.name的值如果设置后,只要窗口还没有关闭,不管页面的url怎么跳转,window.name都不变。
所以,建一个c页面,c页面跟a页面是同域的,a页面用iframe嵌入b页面,在b页面把数据通过window.name赋值,然后b页面通过location.href = c页面的url跳转到c页面,这样子保持a页面和iframe的地址(c页面)是同域的,a页面可以通过iframe.contentWindow.name拿到window.name的值。
在a页面通过监听iframe的
onload事件,设立个标志位,onload事件会执行2次,第一次是b页面加载成功,第二次是c页面加载成功,可以通过iframe.contentWindow.name访问window.name的值。
location.hash + hashchange事件 + 中间页
-
建一个c页面,c页面跟a页面是同域的,a页面用iframe嵌入b页面的url,然后在url再加上hash,这就是传入的数据(因为这样才能触发下面的hashchange事件)
-
b页面监听hashchange事件,触发后取得hash值(传入的数据),然后载入c页面的url(在url加上hash,这就是返回给a页面的数据)
-
c页面和a页面是同域的,c页面可以通过
window.parent.parent找到a页面,然后把数据传回给a页面(调用a页面的方法)。
总结
以上就是解决iframe跨域问题的几种方式。
最推荐是第一种,通过postMessage方式。
如果主域一致,可以设置document.domain。
感谢你们的阅读。