最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • iframe相关及跨域解决方案

    正文概述 掘金(崔小叨)   2021-07-28   1860

    [toc]

    获取iframe的window、document

    页面:

    
    <iframe name="frameSon" id="frame" width="300px" height="300px" src="http://b.laihua.com:4444" frameborder="1"></iframe>
    
    
    1. 通过contentDocument或者contentWindow
    
    const frame = document.getElementById('frame')
    
    const fwindow = frame.contentWindow
    
    const fdoc1 = frame.contentDocument
    
    const fdoc2 = frame.contentWindow.document
    
    

    iframe相关及跨域解决方案

    1. 通过window.frames和iframe的name属性.这种方式直接获取到的是iframe页面的window对象
    
    const frame = window.frames['frameSon']
    
    console.log(`frame:`,frame);
    
    console.log(`frame.document:`,frame.document);
    
    

    iframe相关及跨域解决方案

    读取或者调用iframe内部的方法

    如果要读取或者操作iframe中的内容,要确保在iframe加载完成后再进行操作,如果iframe还未加载完成就开始调用里面的方法或变量,会产生错误。判断iframe是否加载完成有两种方法:

    1. 通过上面第一种方式获取到得iframe是iframeDOM元素,可以直接通过frame.onload回调来判断iframe加载完成

    2. 通过上面第一种方式获取到得iframe是iframe的window对象,但并没有onload方法,所以可以用当前页面的window.onload回调或者 document.readyState=="complete" 来判断

    例如,我们要调用子iframe中的test方法,两种方式:

    第一种方式:

    
    const frame = document.getElementById('frame')
    
    const fdoc = frame.contentDocument || frame.contentWindow.document
    
    frame.onload = () => {
    
    console.log(`frame.contentWindow.test:`, frame.contentWindow.test);
    
    frame.contentWindow.test()
    
    }
    
    

    第二种方式:

    
    const frame = window.frames['frameSon']
    
    const fdoc = frame.document
    
    window.onload = () => {
    
    console.log(`frame.test:`, frame.test);
    
    frame.test()
    
    }
    
    

    主域相同、二级域名不同的两个页面获取对方dom导致的跨域问题

    主页面是a.baidu.com,以iframe的形式引入了b.baidu.com子页面。子页面中声明了一个全局方法test,在父页面中调用这个方法发现跨域了:

    iframe相关及跨域解决方案

    这种情况下,父页面和iframe主域相同,只是二级域名不同,可以通过两个页面同时同时把document.domain设置为相同的主域来解决:

    父页面:

    
    <!DOCTYPE html>
    
    <html lang="en">
    
    <head>
    
    <meta charset="UTF-8">
    
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
    <title>Document</title>
    
    </head>
    
    <body>
    
    <h1>page1</h1>
    
    <iframe name="frameSon" id="frame" width="300px" height="300px" src="http://b.baidu.com:4444"
    
    frameborder="1"></iframe>
    
    </body>
    
    <script>
    
    document.domain = 'baidu.com'
    
    const frame = window.frames['frameSon']
    
    const fdoc = frame.document
    
    window.onload = () => {
    
    console.log(`frame.test:`, frame.test);
    
    frame.test()
    
    }
    
    </script>
    
    </html>
    
    

    iframe子页面:

    
    <!DOCTYPE html>
    
    <html lang="en">
    
    <head>
    
    <meta charset="UTF-8">
    
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
    <title>Document</title>
    
    </head>
    
    <body>
    
    <h1>page2d</h1>
    
    </body>
    
    <script>
    
    document.domain = 'baidu.com'
    
    function test(){
    
    console.log(`222:`,222);
    
    }
    
    </script>
    
    </html>
    
    

    调用结果成功:

    iframe相关及跨域解决方案

    两个页面域名完全不同

    如果两个网页不同源,就无法拿到对方的DOM。例如iframe引入和window.open方法打开的窗口,它们之间的窗口无法通信。

    例如,父页面是baidu.com,子页面是zijie.com,父页面向调用子页面中的全局方法test

    子页面中声明了test全局方法:

    
    function test(){
    
    console.log(`222:`,222);
    
    }
    
    

    父页面通过iframe引入子页面并尝试调用其test方法:

    
    <body>
    
    <h1>page1</h1>
    
    <iframe
    
    name="frameSon"
    
    id="frame"
    
    src="http://zijie.com:4444"
    
    width="300px"
    
    height="300px"
    
    frameborder="1">
    
    </iframe>
    
    </body>
    
    <script>
    
    const frame = window.frames['frameSon']
    
    const fdoc = frame.document
    
    window.onload = () => {
    
    console.log(`frame.test:`, frame.test);
    
    frame.test()
    
    }
    
    </script>
    
    

    结果报错跨域:

    
    (index):54 Uncaught DOMException: Blocked a frame with origin "http://baidu.com:3333" from accessing a cross-origin frame.
    
    at window.onload (http://baidu.com:3333/:54:42)
    
    

    iframe相关及跨域解决方案

    可以通过postMessage、片段标识符、window.name来解决,下面主要介绍前两个:

    window.postMessage

    window.postMessage方法允许跨窗口通信,不论这两个窗口是否同源。

    大概的实现逻辑是:

    一个窗口可以获得对另一个窗口的引用(比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames),调用这个窗口引用上的postMessage方法分发一个MessageEvent 消息。然后子窗口中通过监听message事件来获取父窗口传递的消息即可。

    子页面向父窗口发送消息同理,不同一点是子窗口可以通过MessageEvent.source来获取父窗口的引用。

    语法:

    
    otherWindow.postMessage(message, targetOrigin);
    
    

    otherWindow :要给其发送消息的目标窗口的引用;

    第一个参数message : 具体的消息内容;

    第二个参数targetOrigin指定哪些窗口能接收到消息事件。可以指定接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为"*",表示不限制域名,向所有窗口发送。 在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口。

    监听message时间的时候,可以通过MessageEvent对象获取下面三个参数:

    
    MessageEvent.source:发送消息的窗口引用
    
    MessageEvent.origin: 发送消息的来源origin
    
    MessageEvent.data: 消息内容
    
    

    看一下如何通过postMessage解决上面出现的跨域问题:

    父页面可以获取到子页面的引用后,调用其postMessage方法并将需要调用的方法通过参数传递过去:

    
    window.onload = () => {
    
    frame.postMessage({ fn: 'test' }, "http://zijie.com:4444")
    
    }
    
    

    子页面中监听message时间,并对event.originevent.data作出判断后,调用相应的方法:

    
    function test(){
    
    console.log(`222:`,222);
    
    }
    
    window.onload = function(){
    
    window.addEventListener('message', e => {
    
    console.log(`e:`,e);
    
    if(e.origin === 'http://baidu.com:3333' && e.data.fn === 'test'){
    
    test()
    
    }
    
    })
    
    }
    
    

    执行结果:

    iframe相关及跨域解决方案

    如果子页面执行完方法需要向父页面作出通知,可以在子页面中通过event.source获取父页面的引用,调用其poseMessage方法,然后父页面中通过监听message时间实现。

    子页面:

    
    function test(){
    
    console.log(`222:`,222);
    
    }
    
    window.onload = function(){
    
    window.addEventListener('message', e => {
    
    console.log(`e:`,e);
    
    if(e.origin === 'http://baidu.com:3333' && e.data.fn === 'test'){
    
    test()
    
    // 向父页面发出通知
    
    e.source.postMessage('执行完毕', '*')
    
    }
    
    })
    
    }
    
    

    父页面:

    
    window.onload = () => {
    
    frame.postMessage({ fn: 'test' }, "http://zijie.com:4444")
    
    // 接受子页面通知
    
    window.addEventListener('message', e => {
    
    console.log(`e.data:`,e.data);
    
    })
    
    }
    
    

    执行结果:

    iframe相关及跨域解决方案

    关于postMessage的安全问题

    如果不希望从其他网站接收message,不要为message事件添加任何事件侦听器。 这是一个完全万无一失的方式来避免安全问题。

    如果确实希望从其他网站接收message,始终使用MessageEvent.originMessageEvent.source 属性验证发件人的身份。 任何窗口(包括例如evil.example.com)都可以向任何其他窗口发送消息,

    当使用postMessage将数据发送到其他窗口时,始终指定精确的目标origin,而不是*。 恶意网站可以在我们不知情的情况下更改窗口的位置,因此它可以拦截使用postMessage发送的数据。

    片段标识符fragment identifier

    片段标识符(fragment identifier)指的是URL的#号后面的部分。比如 “example.com/x.html#frag… 的“#fragment”。如果只是改变片段标识符,页面不会重新刷新。

    父页面把需要调用的方法以hash的形式加在iframe地址后面:

    
    const frame = document.getElementById('frame')
    
    window.onload = () => {
    
    // 通过hash改变iframe的src,不会导致刷新
    
    frame.src = `http://zijie.com:4444#test`
    
    }
    
    

    子页面监听hashchage事件来获取当前地址的hash值:

    
    function test(){
    
    console.log(`222:`,222);
    
    }
    
    window.onload = function(){
    
    window.addEventListener('hashchange', e => {
    
    console.log(`e:`,e);
    
    // 通过location.hash获取到父页面传递过来的信息
    
    console.log(`window.location.hash:`,window.location.hash);
    
    const fn = window.location.hash.slice(1)
    
    eval(fn)()
    
    })
    
    }
    
    

    结果:

    iframe相关及跨域解决方案

    同样的,子窗口也可以改变父窗口的片段标识符。

    
    parent.location.href= target + "#" + hash;
    
    

    REF

    (9条消息) postMessage安全性问题_Exploit的小站~-CSDN博客

    前端跨域常见解决方案(包括反向代理)_哔哩哔哩_bilibili

    window.postMessage - Web API 接口参考 | MDN

    利用JS对iframe父子(内外)页面进行操作的方法教程_javascript技巧_脚本之家

    浏览器同源政策及其规避方法 - 阮一峰的网络日志


    起源地下载网 » iframe相关及跨域解决方案

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元