iframe -- postMessage

之前一提到跨域,都是前端到后台的问题.

其实,在网页中嵌套非同源的iframe也存在跨域的问题.

比如,在你自己的页面里利用 iframe 嵌套百度的网页,两个页面存在通信的话,就存在跨域的问题.

对.如果不存在通信,就不存在所谓的跨域问题.


iframe 是干嘛的?

在当前网页中利用 iframe 可以嵌入另一个完整的网页.

这个就是 iframe 干的事情.

这样做的意义在哪呢?为什么我要在我的一个网页里嵌入另外一个网页?

  • 有时候是广告.(别人开发的网页,放在我们网页指定的位置)
  • 有时候是业务逻辑太复杂,需要单独的一个网页去承载.于是把这个比较复杂的逻辑网页就放到网页中了.

其实都是我瞎诌的.为了让自己能够更快的理解这个玩意,编就编吧.


iframe 嵌入自己的网页.

开发web的时候,使用iframe嵌入自己的网页.

这里嵌入自己的网页潜台词就是两个网页是同源的.

我们自己开发的网页,当然是部署在自己的服务器上.

不出意外的话,那协议+域名+端口号都是一致的.

所以,它们是同源.

iframe 的基本语法如下.


项目结构:

image.png

同源 3 个静态页面. 1.html 2.html 3.html

  • 在 1.html 中嵌入 2.html
  • 在 2.html 中嵌入 3.html
image.png

1.html


  

我是1.html网页

2.html


  

我是2.html网页

3.html


  

我是3.html

嵌入完毕之后呢?

光显示的话,也就到这结束了.

如果需要操作同源下嵌套的iframe.
可以按照以下步骤.

1.获取指定的iframe

1.html



let iframe = document.getElementById('demo')

接跟获取一个dom元素一样.利用 getElementById('demo') 即可.

2.获取iframe内部一些关键属性

对于一个完整的html页面来说.
它有window,也有document.

对于嵌套的iframe来说,也不例外.

但是指的注意的是,一定要在等待iframe这个嵌套页面加载完毕之后,在去进行获取.

iframe页面是异步加载的.

// 等待iframe嵌套的页面加载完毕
iframe.onload = function () {
    let iframeWindow = iframe.contentWindow
    let iframeDocument = iframe.contentDocument
}

3. 在同源的情况下

iframe 可以等同于一个普通的DOM节点(不过它是异步加载的)
拿到这个iframe的document和window之后

iframe.onload = function () {
    let iframeWindow = iframe.contentWindow
    let iframeDocument = iframe.contentDocument
    
    iframeDocument.getXXXX ==== 获取或修改嵌套iframe的dom结构.就像操作自己的document一样
    iframeWindow.xxxx === 获取嵌套iframe的方法或者属性或者对象.就像操作自己的window一样.
}

几个注意点:

  • iframe 嵌套获取只能获取一层.(比如1.html 嵌套 iframe(2.html) -> 2.html 嵌套 iframe(3.html).那么 1.html只能获取到2.html 2.html只能获取到3.html

  • window.top 获取当前iframe嵌套层级的最顶级(这里是1.html)

  • window.parent 获取当前iframe的上一层级(2.html就是1.html,3.html就是2.html)

image.png

同源iframe嵌套总结:

  • 可以把嵌套的iframe理解成一个普通的大DOM节点.
  • 它是异步加载的必须等待onload执行完成.
  • 指向完成后拿到 contentWindow 和 contentDocument 之后,就可以无缝的操作了.
  • 注意,iframe只能拿一层.

跨域的iframe通信

两个页面

  • 一个 1.html 链接地址为: http://127.0.0.1:12345/1.html
  • 一个 4.html 链接地址为: http://127.0.0.1:50874/iframe-01/4.html
1.html -> http://127.0.0.1:12345/1.html

   

4.html ---> http://127.0.0.1:50874/iframe-01/4.html

    

我是4.html 端口号50874

它俩的端口号不一致.

12345 | 50874

端口号不一致时,并不影响iframe嵌套.
(经常瞎搞在自己页面里嵌套一个baidu首页)

但是会影响它俩之前的数据跨域请求.

比如,按照同源的方式,去操作iframe的页面,会得到这样一个提示.

1.html -> http://127.0.0.1:12345/1.html
let iframe = document.getElementById('frame')
  iframe.onload = function () {
    const window = iframe.contentWindow
    const document = iframe.contentDocument
    console.log('window',window)
    console.log('document',document)
  }

1.html控制台输出

image.png

发现 document 获取不到.但是获取的到window.

window上有设置了一个全局对象 obj

于是输出:

  let iframe = document.getElementById('frame')
  iframe.onload = function () {
    const window = iframe.contentWindow
    const document = iframe.contentDocument
    console.log('window',window)
    console.log('document',document)
    console.log(window.obj.name)
  }
image.png

很明显的错误提示,操作跨域了.
不能访问跨域iframe的window上的全局属性和方法.
拿不到document(这里为null)了,就更加不能操作dom元素了.

所以,如果嵌套的iframe跨域了,默认情况下只能加载下来看,不能做任何其他的操作.


利用postMessage进行iframe跨域通信

方式一:同一级域名不同二级域名

比如 www.a.com/index.htmlapi.a.com/index.html

由于它们的的一级域名一一致.

可以利用 document.domain 进行跨域操作.

a.html
document.domain = 'a.com'
b.html
document.domain = 'b.com'

双方都设置同样的域之后,就可以像同源非跨域的iframe那样操作了.

方式二.使用postMessage

看了很多博客关于postMessage方法的使用.

大致说的都是:

如果嵌套的iframe存在跨域,那么就可以使用postMessage进行通信.

于是心想,这也太简单了吧.

就开始吭哧吭哧写代码.

image.png
1.html -> http://127.0.0.1:12345/1.html

  
  

我是1.html

4.html ->http://127.0.0.1:50874/iframe-01/4.html


  

我是4.html

现在,我想让 1.html 跨域的给 2.html 传递数据.

于是在 1.html 中.

window.postMessage('1.html的数据','http://127.0.0.1:50874/')

在 4.html 中

  window.addEventListener('message', function (e) {
    console.log(e.data)
  },false)

想着非常完美,也太简单了.

执行浏览器.

image.png

发现报错了.

回想一下:

  • 1.html
  • 1.html 中利用 iframe 嵌套了 2.html
  • 它俩的属于不同的域(端口号不同)
  • 现在我想从 1.html 传递数据到 2.html.
  • 1.html 里面使用 window.postMessage() 发现报错了.

问题出在哪?

重新查看API文档之后,发现理解是错的.

本质上利用 postMessage 跨域,不是 1.html2.html 发数据.

应该是是 2.html2.html 发数据

体现在代码上应该是就是:

1.html -> http://127.0.0.1:12345/1.html
window.onload = function () {
    let frame = document.getElementById('frame')
    // 相当于还是自己在给自己传啊!!!
    document.getElementById('text').addEventListener('input', function () {
      frame.contentWindow.postMessage(this.value, 'http://127.0.0.1:50874/')
    }, false)
  }

传递数据的是 frame.contentWindow.postMessage
而不是想当然的 window.postMessage

测试一下想法.

1.html中 --> http://127.0.0.1:12345/1.html

1.html -> http://127.0.0.1:12345/1.html


  
  

我是1.html

1.html -> http://127.0.0.1:12345/1.html

在4.html中 --> ->http://127.0.0.1:50874/iframe-01/4.html

4.html -> http://127.0.0.1:50874/iframe-01/4.html


  

我是4.html

4.html->http://127.0.0.1:50874/iframe-01/4.html

查看结果:

image.png

最后总结:

  • 跨域不光是前台都后台的跨域.
  • iframe嵌套不同源的资源也存在跨域.
    • 如果一级域名相同,可以使用document.domain 进行跨域操作.
    • 如果域名完全不同,可以使用 postMessage 进行跨域.
      • postMessage 传递数据,本质上仍然是自己给自己传
      • 在给某个iframe传递数据时,必须首先拿到当前iframe的contentWindow.然后在contentWindow上使用postMessage 传递数据.

码云code

你可能感兴趣的:(iframe -- postMessage)