跳至主要內容

概述

Yang大约 6 分钟

弹窗

  • 弹窗是一个独立的窗口,具有自己的独立 JavaScript 环境。因此,使用弹窗打开一个不信任的第三方网站是安全的
  • 弹窗可以导航(修改 URL),并将消息发送到 opener 窗口
  • 如果弹窗是在用户触发的事件处理程序(如 onclick)之外调用的,大多数浏览器都会阻止此类弹窗

打开新的弹窗

语法:window.open(url, name, param)

默认情况下,浏览器会打开一个新标签页,但如果提供了窗口大小,那么浏览器将打开一个弹窗

  • url:要在新窗口加载的 URL

  • name:新窗口的名称,每个窗口都有一个 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

承载了一个单独的嵌入的窗口,它具有自己的 documentwindow

当访问嵌入的窗口中的东西时,浏览器会检查 iframe 是否具有相同的源。如果不是,则会拒绝访问(对 location 进行写入是一个例外,它是会被允许的)

【MDN文档】open in new window 【点击劫持攻击】open in new window

  • 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

允许窗口之间相互通信,无论它们来自什么源,是解决“同源”策略的方式之一

【MDN文档】open in new window

  • 发送方语法:otherWin.postMessage(data, targetOrigin)
    • otherWin:接收方 window
    • data:要发送的数据。可以是任何对象,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>
上次编辑于:
贡献者: sunzhenyang