跨窗口通信
- 一个 iframe 标签承载了一个单独的嵌入的窗口,它具有自己的 document 和 window。
- iframe.contentWindow 来获取 iframe 中的 window。
- iframe.contentDocument 来获取 iframe 中的 document,是 iframe.contentWindow.document 的简写形式。
- 当我们访问嵌入的窗口中的东西时,浏览器会检查 iframe 是否具有相同的源。如果不是,则会拒绝访问(对 location 进行写入是一个例外,它是会被允许的)。
- 通过 iframe.contentWindow 获取对内部 window 的引用 —— 这是被允许的。
- 可以对 location 进行写入
- 如果 iframe 具有相同的源,可以做任何事情
- iframe.onload vs iframe.contentWindow.onload
- iframe.onload 事件(在 iframe 标签上)与 iframe.contentWindow.onload(在嵌入的 window 对象上)基本相同。当嵌入的窗口的所有资源都完全加载完毕时触发。
- ……但是,无法使用 iframe.contentWindow.onload 访问不同源的 iframe。因此,请使用 iframe.onload
- 子域上的 window:document.domain
- 如果窗口的二级域相同,例如 john.site.com,peter.site.com 和 site.com(它们共同的二级域是 site.com),我们可以使浏览器忽略该差异,使得它们可以被作为“同源”的来对待,以便进行跨窗口通信。
- 为了做到这一点,每个这样的窗口都应该执行下面这行代码:
- document.domain = 'site.com';
- 这样就可以了。现在它们可以无限制地进行交互了。但是再强调一遍,这仅适用于具有相同二级域的页面。
- 已弃用,但仍有效,document.domain 属性正在被从 规范 中删除。
- 不应该对尚未加载完成的 iframe 的文档进行处理,因为那是 错误的文档。
- 如何检测文档就位(加载完成)的时刻呢?正确的文档在 iframe.onload 触发时肯定就位了。但是,只有在整个 iframe 和它所有资源都加载完成时,iframe.onload 才会触发。
- 集合:window.frames,获取 iframe 的 window 对象的另一个方式是从命名集合 window.frames 中获取:
- 通过索引获取:window.frames[0] —— 文档中的第一个 iframe 的 window 对象。
- 通过名称获取:window.frames.iframeName —— 获取 name="iframeName" 的 iframe 的 window 对象。
- 一个 iframe 内可能嵌套了其他的 iframe。相应的 window 对象会形成一个层次结构(hierarchy)。
- window.frames —— “子”窗口的集合(用于嵌套的 iframe)。
- window.parent —— 对“父”(外部)窗口的引用。
- window.top —— 对最顶级父窗口的引用。
window.frames[0].parent === window; // true
- 可以使用 top 属性来检查当前的文档是否是在 iframe 内打开的:
if (window == top) { // 当前 window == window.top?
alert('The script is in the topmost window, not in a frame');
} else {
alert('The script runs in a frame!');
}
跨窗口通信
postMessage 接口允许窗口之间相互通信,无论它们来自什么源。前提是它们双方必须均同意并调用相应的 JavaScript 函数。