背景:
最近项目里遇到一个问题,我们的项目里使用iframe嵌套了一个第三方网站(airflow),同事希望能在项目里获取到iframe里的路由,同步到项目里。事后觉得并不可行,iframe本质是用于隔离两个系统的,让外界不用知道iframe内部细节即可展示,获取iframe的路由是本末倒置。但在探索过程中学到了很多,下面总结。
什么是iframe?
MDN中定义是:嵌套的浏览上下文(nested browser context) ,百度百科定义:文档中的文档
隔离了网站内部逻辑、资源,只需src指向即可
权限
iframe的使用没有权限限制,可以随意指向任何你可访问的网站(百度、本地文件、 本地项目)
(那会存在安全问题?嵌入iframe假冒官方网站)
<iframesrc='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里才能获取。
<iframeid='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.contentWindowiframeWindow.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给父页面即可~
