window.parent
获取上一级的window对象。 如果当前窗口是一个 <iframe>
, <object>
, 或者 <frame>
,则它的父窗口是嵌入它的那个窗口。如果还是iframe则是该iframe的window对象, 如果没有parent,则返回自身的引用。
window.top 获取最顶级容器的window对象,即,就是你打开页面的window
window.self 返回自身window的引用。可以理解 window===window.self
window.frames 当前窗口的所有直接子窗口
window.open/window.opener 使用window.open返回的对象
在iframe内获取iframe外的内容
window.parent
window.parent
获取上一级的window对象。 如果当前窗口是一个 <iframe>
, <object>
, 或者 <frame>
,则它的父窗口是嵌入它的那个窗口。如果还是iframe则是该iframe的window对象, 如果没有parent,则返回自身的引用。
window.top 获取最顶级容器的window对象,即,就是你打开页面的window
例如,想在iframe内获取所有parent的userAgent
,检查是否含有
HTML结构:
parent.html
iframe child1
iframe child2
执行结果:
用本地文件测试frame的时候,在执行curWindow.navigator.userAgent
的时候会报错:
Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame.
解决方法: 关闭浏览器CORS
点击展开parent.html代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 <!DOCTYPE html> <html > <head > <meta charset ="UTF-8" > <title > parent</title > <style > input { width: 97%; } iframe { width: 100%; } </style > </head > <body > <script type ="text/javascript" > function setUserAgent (window, customUserAgent) { const newUserAgent = `${navigator.userAgent} ${customUserAgent} ` ; if (navigator.__defineGetter__) { navigator.__defineGetter__('userAgent' , function () { return newUserAgent; }); } else if (Object .defineProperty) { Object .defineProperty(navigator, 'userAgent' , { get : function () { return newUserAgent; }, }); } if (window .navigator.userAgent.indexOf(customUserAgent) < 0 ) { const userAgentProp = { get : function () { return newUserAgent; }, }; try { Object .defineProperty(window .navigator, 'userAgent' , userAgentProp); } catch (e) { window .navigator = Object .create(navigator, { userAgent: userAgentProp, }); } } } setUserAgent(window , 'CustomUserAgent/1.0' ); const a = document .createElement('input' ); a.value = 'parent ' + window .navigator.userAgent; document .body && document .body.appendChild(a); let isCustomUserAgent = window .navigator && window .navigator.userAgent.indexOf('CustomUserAgent' ) >= 0 ; let curWindow = window ; while (window .top !== curWindow) { curWindow = curWindow.parent; const newUserAgent = curWindow.navigator.userAgent; if (newUserAgent && newUserAgent.indexOf('CustomUserAgent' ) >= 0 ) { isCustomUserAgent = true ; break ; } } const b = document .createElement('input' ); b.value = 'isCustomUserAgent ' + isCustomUserAgent; document .body && document .body.appendChild(b); </script > <iframe src ="child1.html" > </iframe > </body > </html >
点击展开child1.html代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 <!DOCTYPE html> <html > <head > <meta charset ="UTF-8" /> <title > child1</title > <style > input { width: 97%; } iframe { width: 100%; } </style > </head > <body > <script type ="text/javascript" > const currUserAgent = document .createElement('input' ); currUserAgent.value = 'child1 ' + window .navigator.userAgent; document .body && document .body.appendChild(currUserAgent); let isCustomUserAgent = window .navigator && window .navigator.userAgent.indexOf('CustomUserAgent' ) >= 0 ; let curWindow = window ; let isParentPowerPoint; try { while (window .top !== curWindow) { curWindow = curWindow.parent; const newUserAgent = curWindow.navigator.userAgent; if (newUserAgent && newUserAgent.indexOf('CustomUserAgent' ) >= 0 ) { isCustomUserAgent = true ; break ; } } } catch (error) { console.log (error ); } const b = document .createElement('input' ); b.value = 'isCustomUserAgent from ancestor ' + isCustomUserAgent; document .body && document .body.appendChild(b); </script > <iframe src ="child2.html" > </iframe > </body > </html >
点击展开child2.html代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <!DOCTYPE html> <html > <head > <meta charset ="UTF-8" /> <title > child2</title > <style > input { width: 97%; } iframe { width: 100%; } </style > </head > <body > <script type ="text/javascript" > const currUserAgent = document .createElement('input' ); currUserAgent.value = 'child2 ' + window .navigator.userAgent; document .body && document .body.appendChild(currUserAgent); let isCustomUserAgent = window .navigator && window .navigator.userAgent.indexOf('CustomUserAgent' ) >= 0 ; let curWindow = window ; while (window .top !== curWindow) { curWindow = curWindow.parent; const newUserAgent = curWindow && curWindow.navigator.userAgent; if (newUserAgent && newUserAgent.indexOf('CustomUserAgent' ) >= 0 ) { isCustomUserAgent = true ; break ; } } const b = document .createElement('input' ); b.value = 'isCustomUserAgent from ancestor ' + isCustomUserAgent; document .body.appendChild(b); </script > </body > </html >
父页面向子页面传递数据by URL
利用location对象的hash值,通过它传递通信数据。在父页面设置iframe
的src
后面多加个data
字符串,然后在子页面中通过某种方式能即时的获取到这儿的data。
在iframe外获取iframe里的内容
方法一 contentWindow
和contentDocument
iframe.contentWindow
可以获取iframe的window对象, MDN
iframe.contentDocument
可以获取iframe的document对象, MDN 。
1 2 3 4 5 6 7 8 9 10 11 12 13 const iframe = document .querySelector("iframe" );const iContentWindow = iframe.contentWindow;const idoc = iContentWindow.document;const iContentDocument = iframe.contentDocument;const isSameDoc = iframe.contentDocument === iframe.contentWindow.document; console .log("iframe.contentWindow" , iContentWindow); console .log("iframe.contentDocument" , iContentDocument); console .log("html" , iContentDocument.documentElement);
方法二 window.frames
结合Name属性,通过window提供的frames获取
1 2 3 4 5 6 7 <iframe src ="/index.html" id ="ifr1" name ="ifr2" scrolling ="yes" > <p > Your browser does not support iframes.</p > </iframe > <script type ="text/javascript" > console .log(window .frames['ifr2' ].window); console .dir(document .getElementById("iframe" ).contentWindow); </script >
跨域通信
跨域
Cross-Origin Resource Sharing (CORS)
CORS的基本原理请看上一篇:
CORS的基本原理
为了保证用户信息的安全,95年的时候Netscape公司引进了同源策略,里面的同源指的是三个相同:
协议(例如 https, http)
端口号(例如 8080,8443,443)
域名 (document.domain
设置为相同的值)
违反了同源策略就会出现跨域问题,主要表现为以下三方面:
无法读取cookie、localStorage、indexDB
DOM无法获得
ajax请求无法发送
can’t access an
iframe缺点以及解决方案
缺点:
iframe会阻塞主页面的Onload事件;要确保在iframe加载完成后再进行操作,如果iframe还未加载完成就开始调用里面的方法或变量,会产生错误;
1 2 3 iframe.onload = function ( ) { }
相同域iframe和主页面共享http连接池,所以如果相同域用多个iframe会阻塞加载
解决方案: 动态生成iframe,在主页面加载完成后去生产iframe加载,从而避免阻塞的影响
iframe 对SEO不友好
解决方案:在广告位以及内部系统等适合的场景中使用iframe
如果iframe所链接的是外部页面,因为安全机制不能使用同域名下的通信方式;
跨域通信方法一设置document.domain
例子:需要用iframe引入一个别人封装好的类似视频播放器的东西。iframe里面有一个全屏的按钮,点击后需要页面让iframe全屏,由于受到同源策略的限制,iframe无法告诉页面全屏。
document.domain
作用是获取/设置当前文档的原始域部分,同源策略会判断两个文档的原始域是否相同来判断是否跨域。这意味着只要把这个值设置成一样就可以解决跨域问题了。
在此将domain设置为一级域名的值,a页面url为a.demo.com
,a页面中iframe引用的b页面url为b.demo.com
,具体设置为document.domain = 'demo.com'
设置完之后,在a页面的window上挂载使iframe全屏的方法
1 2 3 4 window .toggleFullScreen = () => { }
在b页面上可以直接获取到a页面的window对象并直接调用
1 2 window .parent.toggleFullScreen()
但是这个值的设置也有一定限制,只能设置为当前文档的上一级域或者是跟该文档的URL的domain一致的值。如url为a.demo.com
,那domain就只能设置为demo.com
或者a.demo.com
。因此,设置domain的方法只能用于解决主域相同而子域不同 的情况。
跨域通信方法二使用中间页面
我们还可以使用一个与a页面同域名但不同路由的c页面作为中间页面,b页面加载c页面,c页面调用a页面的方法,从而实现b页面调用a页面的方法。
在a页面的node层新开一个路由,此路由加载一个c页面作为中间页面,c页面的url为a.demo.com/c。c页面只是一个简单的html页面,在window的onload事件上调用了a页面的方法。
a页面 a.demo.com
iframe b页面
iframe c页面 【中间页a.demo.com/c
】(与a页面同域名但不同路由, c页面调用a页面的方法,从而实现b页面调用a页面的方法)
由于c页面和a页面是符合同源策略的,所以可以避开跨域问题,执行全屏的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <!DOCTYPE html> <html lang ="en" > <head > <meta charset ="UTF-8" > <title > </title > </head > <body > <script > window .onload = function ( ) { parent.parent.toggleFullScreen(); } </script > </body > </html >
由于c页面和a页面是符合同源策略的,所以可以避开跨域问题,执行全屏的方法。
跨域通信方法三window.postMessage
window.postMessage方法可以安全地实现跨源通信 ,写明目标窗口的协议、主机地址或端口就可以发信息给它。
1 2 3 4 5 parent.postMessage( value, "http://a.demo.com" );
1 2 3 4 5 6 window .addEventListener("message" , function ( event ) { if (event.origin !== 'http://b.demo.com' ) return ; toggleFullScreen() });
兼容性
需要注意的是postMessage API中的message在ie8/ie9等一些低版本浏览器中,中是不支持除String以外的其他类型时(因为不支持结构化克隆算法),所以,如果要兼容低版本ie,需要通过JSON.strigify以及JSON.parse去发送和接受。
Fix CORS
Disable CORS limit
用本地文件测试frame的时候,在执行curWindow.navigator.userAgent
的时候会报错:
Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame.
原因是浏览器默认开启了CORS的安全保护,不允许iframe跨域获取一些值。
关闭同源策略 (CORS) 只会影响您的浏览器。此外,在浏览器中禁用同源安全设置会允许任何网站访问跨源资源,这是非常危险的,只能用于开发目的。现在,只有 window.postMessage()
才是frame/iframe
之间交互的最佳方式。
解决方案1 关闭浏览器CORS limit [临时解决]
参考 Disable same origin policy in Chrome , 命令行加参数--disable-web-security
打开chrome浏览器
1 2 3 4 5 6 7 8 9 10 open /Applications/Google\ Chrome.app --args --user-data-dir="/const/tmp/Chrome dev session" --disable -web-security $ google-chrome --disable -web-security chrome.exe --disable -web-security
解决方案2 Extensions “xampp” or “Live Server” [临时解决]
You could solve it by installing xampp and moving all files to htdocs or using an extension like “Xampp”.
解决方案3 try-catch [跨域时无法获取想要的值,只是不抛错]
1 2 3 4 5 6 try { const parent = window .parent && window .parent.navigator; } catch (error) { console .warn(`Error - ${error} ` ); }
解决方案4 window.postMessage()
[终极解决]
同源策略可防止脚本访问不同源的网站内容,您可以使用 window.postMessage()
安全地在 Window 对象之间实现跨源通信。
1 2 postMessage(message,targetOrigin) postMessage(message,targetOrigin, [transfer])
参考文章
Donate
WeChat Pay
Alipay
本文由
Yuankun Li 创作和发表,采用
BY -
NC -
SA 国际许可协议进行许可,转载请注明作者及出处。