概述
弹窗
- 弹窗是一个独立的窗口,具有自己的独立 JavaScript 环境。因此,使用弹窗打开一个不信任的第三方网站是安全的
- 弹窗可以导航(修改 URL),并将消息发送到 opener 窗口
- 如果弹窗是在用户触发的事件处理程序(如
onclick
)之外调用的,大多数浏览器都会阻止此类弹窗
打开新的弹窗
语法:
window.open(url, name, param)
默认情况下,浏览器会打开一个新标签页,但如果提供了窗口大小,那么浏览器将打开一个弹窗
url
:要在新窗口加载的 URLname
:新窗口的名称,每个窗口都有一个window.name
,可以指定哪个窗口用于弹窗,如果已经有一个这样名字的窗口,将在该窗口打开给定的 URL,否则会打开一个新窗口param
:新窗口的配置字符串,它包括设置,用逗号分隔,参数之间不能有空格,例如:width=200,height=100
(多屏环境下,某些参数会失效)- 位置
left/top
(数字):屏幕上窗口的左上角的坐标。这有一个限制:不能将新窗口置于屏幕外width/height
(数字):新窗口的宽度和高度。宽度/高度的最小值是有限制的,因此不可能创建一个不可见的窗口
- 窗口功能(参数值均为
yes/no
)menubar
:显示或隐藏新窗口的浏览器菜单toolbar
:显示或隐藏新窗口的浏览器导航栏(后退,前进,重新加载等)location
:显示或隐藏新窗口的 URL 字段。Firefox 和 IE 浏览器不允许默认隐藏它status
:显示或隐藏状态栏。同样,大多数浏览器都强制显示它resizable
:允许禁用新窗口大小调整。不建议使用scrollbars
:允许禁用新窗口的滚动条。不建议使用
- 位置
参数规则
- 如果
open
调用中没有第三个参数,或者它是空的,则使用默认的窗口参数 - 如果这里有一个参数字符串,但是某些
yes/no
功能被省略了,那么被省略的功能则被默认值为no
。因此,如果你指定参数,请确保将所有必需的功能明确设置为yes
- 如果参数中没有
left/top
,那么浏览器会尝试在最后打开的窗口附近打开一个新窗口 - 如果没有
width/height
,那么新窗口的大小将与上次打开的窗口大小相同
操作新窗口
如果主窗口和弹窗同源,那么它们可以彼此自由地读取和修改。否则,它们可以更改彼此的地址(location),交换消息
let newWin = open('http://localhost/test.html', 'test', params);
// 重写新窗口内容
newWin.document.write("Hello, world!");
// 获取新窗口内容
newWin.onload = function(){
console.log(newWin.document.body.innerHTML)
}
从弹窗访问窗口
// 弹窗窗口通过 window.opener 获取父窗口对象
window.opener.document.body.innerHTML = '<h1>新页面修改</h1>'
关闭窗口
window.closed
:检查窗口是否被关闭window.close()
:关闭窗口- 适用于任何
window
,如果window
不是通过window.open()
创建的,那么大多数浏览器都会忽略window.close()
。因此,close()
只对弹窗起作用
- 适用于任何
调整窗口
为了防止滥用,浏览器通常会阻止这些方法。它们仅在我们打开的,没有其他选项卡的弹窗中能够可靠地工作
JavaScript 无法最小化或者最大化一个窗口。这些操作系统级别的功能对于前端开发者而言是隐藏的
移动或者调整大小的方法不适用于最小化/最大化的窗口
win.moveBy(x,y)
:将窗口相对于当前位置向右移动x
像素,并向下移动y
像素。允许负值(向上/向左移动)win.moveTo(x,y)
:将窗口移动到屏幕上的坐标(x,y)
处win.resizeBy(width,height)
:根据给定的相对于当前大小的width/height
调整窗口大小。允许负值win.resizeTo(width,height)
:将窗口调整为给定的大小
跨窗口通信
同源
如果两个 URL 具有相同的协议,域和端口,则称它们是“同源”的
如果有对另外一个窗口(例如:一个使用
window.open
创建的弹窗,或者一个窗口中的iframe
)的引用,并且该窗口是同源的,则具有对该窗口的全部访问权限否则,如果该窗口不是同源的,则无法访问该窗口中的内容:变量,文档,任何东西。
- 唯一的例外是
location
:可以修改它(进而重定向用户)。但是无法读取location
(因此,无法看到用户当前所处的位置,也就不会泄漏任何信息)
- 唯一的例外是
iframe
承载了一个单独的嵌入的窗口,它具有自己的
document
和window
当访问嵌入的窗口中的东西时,浏览器会检查
iframe
是否具有相同的源。如果不是,则会拒绝访问(对location
进行写入是一个例外,它是会被允许的)
iframe.contentWindow
:获取<iframe>
中的window
iframe.contentDocument
:获取<iframe>
中的document
,是iframe.contentWindow.document
的简写形式- 一个 iframe 内可能嵌套了其他的 iframe。相应的
window
对象会形成一个层次结构window.frames
:“子”窗口的集合(用于嵌套的 iframe)window.parent
:对“父”(外部)窗口的引用window.top
:对最顶级父窗口的引用
<iframe src="https://localhost/test.html" id="iframe"></iframe>
<script>
iframe.onload = function() {
// 获取对内部 window 的引用
let iframeWindow = iframe.contentWindow; // OK
try {
// 无法获取其中的文档
let doc = iframe.contentDocument; // ERROR
} catch(e) {
alert(e); // Security Error(另一个源)
}
// 并且无法读取 iframe 中页面的 URL
try {
// 无法从 location 对象中读取 URL
let href = iframe.contentWindow.location.href; // ERROR
} catch(e) {
alert(e); // Security Error
}
// 可以写入 location(所以,在 iframe 中加载了其他内容)!
iframe.contentWindow.location = '/'; // OK
iframe.onload = null; // 清空处理程序,在 location 更改后不要再运行它
};
</script>
二级域名下的通信
需要再每个窗口中添加如下代码,使浏览器忽略该差异,使得它们可以被作为“同源”的来对待,以便进行跨窗口通信
// 已弃用,但仍有效
document.domain = 'http://localhost';
postMessage
允许窗口之间相互通信,无论它们来自什么源,是解决“同源”策略的方式之一
- 发送方语法:
otherWin.postMessage(data, targetOrigin)
(otherWin
:接收方 windowdata
:要发送的数据。可以是任何对象,IE 浏览器只支持字符串,因此需要对复杂的对象调用JSON.stringify
方法进行处理,以支持该浏览器targetOrigin
:接收方的源,以便只有来自给定的源的窗口才能获得该消息- 如果不做这个检查,可以将
targetOrigin
设置为*
- 如果不做这个检查,可以将
- 接收方使用
onmessage
事件接收,参数event
相关参数如下event.data
:传递过来的数据event.prigin
:发送方的源event.source
:对发送方窗口的引用(发送方 window)
<!-- index.html 发送方 http://127.0.0.1:56082 -->
<script>
newWin = open('http://127.0.0.1:64361', 'test', params);
setInterval(function() {
newWin.postMessage(JSON.stringify({
a: 1,
b: 2
}), 'http://127.0.0.1:64361')
}, 2000)
</script>
<!-- test.html 接收方 http://127.0.0.1:64361 -->
<script>
window.addEventListener("message", function(event) {
console.log(event)
console.log(event.data)
// 可以使用 event.source.postMessage(...) 向回发送消息
// event.origin http://127.0.0.1:56082/
event.source.postMessage('反向发送信息', event.origin)
});
</script>