背景:
最近项目里遇到一个问题,我们的项目里使用iframe嵌套了一个第三方网站(airflow),同事希望能在项目里获取到iframe里的路由,同步到项目里。事后觉得并不可行,iframe本质是用于隔离两个系统的,让外界不用知道iframe内部细节即可展示,获取iframe的路由是本末倒置。但在探索过程中学到了很多,下面总结。

什么是iframe?

MDN中定义是:嵌套的浏览上下文(nested browser context)百度百科定义:文档中的文档
隔离了网站内部逻辑、资源,只需src指向即可

权限

iframe的使用没有权限限制,可以随意指向任何你可访问的网站(百度、本地文件、 本地项目)
(那会存在安全问题?嵌入iframe假冒官方网站)

  1. <iframe
  2. src='https://www.baidu.com/'
  3. id='baidu'
  4. height="1500"
  5. width="100%"
  6. name='baidu'
  7. ></iframe>

image.png

跨域

嵌入https文档后会报错:Uncaught DOMException: Blocked a frame with origin “https://www.baidu.com“ from accessing a cross-origin frame.
image.png
是出现了跨域
网上都说非同源的会报错,但是多个测试嵌入http的且非同源的都不报错???只有https的报错

onload事件

嵌入文档加载完毕后触发,注意⚠️在onload里的window、document都是父页面的。

  1. <iframe src='https://www.baidu.com/' id='baidu' name='baidu'></iframe>
  2. document.getElementById('baidu').onload = function (e) {
  3. console.log('onload', document)
  4. }

获取iframe的dom

1.在父页面中获取iframe的window

  1. window.frames[0]
  2. window.frames[‘local’]
  3. window.frames.local
  4. document.getElementById(‘iframe’).contentWindow

    2.在iframe中获取父页面的window

  5. window.top

  6. window.parent

注意:获取iframe的window、document不能直接在js里获取,因为有加载时间,所以需要在onload里才能获取。

  1. <iframe
  2. id='iframe'
  3. src='http://127.0.0.1:8899/'
  4. onLoad={() => {
  5. const iframe = document.getElementById('iframe')
  6. console.log('test', iframe.contentWindow)
  7. }}
  8. />

获取iframe的dom时会出现因为跨域获取不到,

非常奇怪,在各种不同情况下跨域情况各不相同,在mac的for-react项目里console.log iframe.contentWindow就会报错,获取contentWindow不会报错,而autox的xFlow项目里iframe.contentWindow不会报错,但内容都是空,且进一步获取其他对象就会报错。这个问题先搁置,无论如何,反正访问iframe的dom就会报跨域的错。

跨域通信

postMessage

最好用的方法,详情可见MDN

  1. // 发送方:
  2. otherWindow.postMessage(message, targetOrigin)
  3. // 接收方:
  4. const receiveMessage = (event) => {
  5. console.log(event)
  6. }
  7. window.addEventListener('message', receiveMessage)

接收的消息在消息事件的data属性里:

  1. MessageEvent {
  2. isTrusted: true,
  3. data: 'this message',
  4. origin: "http://127.0.0.1:8888",
  5. lastEventId: "",
  6. source: Window,
  7. }

本地html文件之间即可发送

父页面向子页面发消息

  1. <!-- parent.html -->
  2. <iframe src='./child.html' id='iframe'></iframe>
  3. <script type="text/javascript">
  4. const iframe = document.getElementById('iframe')
  5. iframe.onload = function (e) {
  6. const iframeWindow = iframe.contentWindow
  7. iframeWindow.postMessage('hello child iframe', '*')
  8. }
  9. </script>
  10. <!-- child.html -->
  11. <script type="text/javascript">
  12. const receiveMessage = (e) => {
  13. console.log('got it', e)
  14. }
  15. window.addEventListener('message', receiveMessage, false)
  16. </script>

那么回到最初的问题,我想在父页面里拿到iframe的路由的方式,即在iframe里发送window.location.href给父页面即可~