背景:
最近项目里遇到一个问题,我们的项目里使用iframe嵌套了一个第三方网站(airflow),同事希望能在项目里获取到iframe里的路由,同步到项目里。事后觉得并不可行,iframe本质是用于隔离两个系统的,让外界不用知道iframe内部细节即可展示,获取iframe的路由是本末倒置。但在探索过程中学到了很多,下面总结。
什么是iframe?
MDN中定义是:嵌套的浏览上下文(nested browser context) ,百度百科定义:文档中的文档
隔离了网站内部逻辑、资源,只需src指向即可
权限
iframe的使用没有权限限制,可以随意指向任何你可访问的网站(百度、本地文件、 本地项目)
(那会存在安全问题?嵌入iframe假冒官方网站)
<iframe
src='https://www.baidu.com/'
id='baidu'
height="1500"
width="100%"
name='baidu'
></iframe>
跨域
嵌入https文档后会报错:Uncaught DOMException: Blocked a frame with origin “https://www.baidu.com“ from accessing a cross-origin frame.
是出现了跨域
网上都说非同源的会报错,但是多个测试嵌入http的且非同源的都不报错???只有https的报错
onload事件
嵌入文档加载完毕后触发,注意⚠️在onload里的window、document都是父页面的。
<iframe src='https://www.baidu.com/' id='baidu' name='baidu'></iframe>
document.getElementById('baidu').onload = function (e) {
console.log('onload', document)
}
获取iframe的dom
1.在父页面中获取iframe的window
- window.frames[0]
- window.frames[‘local’]
- window.frames.local
document.getElementById(‘iframe’).contentWindow
2.在iframe中获取父页面的window
window.top
- window.parent
注意:获取iframe的window、document不能直接在js里获取,因为有加载时间,所以需要在onload里才能获取。
<iframe
id='iframe'
src='http://127.0.0.1:8899/'
onLoad={() => {
const iframe = document.getElementById('iframe')
console.log('test', iframe.contentWindow)
}}
/>
获取iframe的dom时会出现因为跨域获取不到,
非常奇怪,在各种不同情况下跨域情况各不相同,在mac的for-react项目里console.log iframe.contentWindow就会报错,获取contentWindow不会报错,而autox的xFlow项目里iframe.contentWindow不会报错,但内容都是空,且进一步获取其他对象就会报错。这个问题先搁置,无论如何,反正访问iframe的dom就会报跨域的错。
跨域通信
postMessage
最好用的方法,详情可见MDN
// 发送方:
otherWindow.postMessage(message, targetOrigin)
// 接收方:
const receiveMessage = (event) => {
console.log(event)
}
window.addEventListener('message', receiveMessage)
- otherWindow:接收消息的iframe的window;
- message:传递的消息;
- targetOrigin:‘*’或‘http://localhost:4321’,表示那些origin的窗口可接收该消息;
接收的消息在消息事件的data属性
里:
MessageEvent {
isTrusted: true,
data: 'this message',
origin: "http://127.0.0.1:8888",
lastEventId: "",
source: Window,
…
}
本地html文件之间即可发送
父页面向子页面发消息
<!-- parent.html -->
<iframe src='./child.html' id='iframe'></iframe>
<script type="text/javascript">
const iframe = document.getElementById('iframe')
iframe.onload = function (e) {
const iframeWindow = iframe.contentWindow
iframeWindow.postMessage('hello child iframe', '*')
}
</script>
<!-- child.html -->
<script type="text/javascript">
const receiveMessage = (e) => {
console.log('got it', e)
}
window.addEventListener('message', receiveMessage, false)
</script>
那么回到最初的问题,我想在父页面里拿到iframe的路由的方式,即在iframe里发送window.location.href给父页面即可~