笔记-window.open用法

9,339 阅读6分钟

window.open用法详解

目录

window.open

1. 参数

window.open常用来在新的window或新的tab页打开一个页面或文件(如图片、PDF等),它支持三个参数:

strUrl:要打开的页面或资源的url地址。 strWindowName:窗口的名字,用于后续对该窗口的引用,不是窗口的标题。 strWindowFeatures:窗口的描述参数,如尺寸、位置、是否启用工具栏等。 该方法的返回值是新打开的窗口的引用,也就是新窗口的window对象。在遵循同源策略的情况下,可以直接通过该对象访问被打开的页面;即使在跨域的情况下,也可以通过window.postMessage向其发消息。

我们分别来解读这三个参数的用法:

(1)strUrl

打开的窗口中要加载的url,可以是一个HTML页面,或者其他任何浏览器能打开的资源文件。

要加载的url可以是同域的,也可以是跨域的。在跨域条件下,window.open满足和跨域的iframe一样的限制。从通信的角度来说,使用window.open打开窗口和在内嵌的iframe内打开页面是等价的,两者的差异更多是在于视觉效果的不同。

strUrl允许传入空值。此时第二个参数必须传入一个已打开的窗口的名字,从而获取这个窗口的引用:

// 打开一个窗口
let win = window.open('https://www.baidu.com', 'baidu');
// 通过窗口名获取上述窗口
let refWin = window.open('', 'baidu');

这样就可以在不打开新窗口的情况下获得窗口名为'baidu'的窗口的引用。

(2)strWindowName

该参数是被打开的窗口的名字,注意,它并不是窗口的标题。该参数只是一个窗口标识,用于以后通过它来找到对应的窗口的引用。

比如在上面的例子中,我们可以通过'baidu'这个名字来找到刚打开的百度的窗口,这样就不需要在全局变量中保存该窗口的引用了(它的代价不在于内存损耗,而在于对全局变量的维护)。

该参数除了支持普通的名字外,还支持和a标签一样的特殊关键字:_self、_blank、_parent和_top,分别用于在当前窗口、空白窗口、父窗口和顶级窗口中打开该窗口,具体的行为请参考a标签的target属性

当传入了已经被使用过的窗口的名字时,不会新打开一个窗口,而是在该名字对应的窗口中打开,该窗口之前加载的内容会被替换。

// 在上述窗口打开csdn首页
window.open('https://csdn.net', 'baidu');

如果总是想打开新页面,可以给第二个参数传入'_blank'。不过此时如果需要引用这些窗口,需要即时保存窗口的引用。

(3) strWindowFeatures

窗口参数描述,字符串类型,各个参数由逗号隔开,参数之间以等号连接。比如下面的例子可以在距当前window左上角(10, 10)位置处打开一个宽度400像素,高度200像素的窗口:

window.open('https://www.baidu.com', 
  'baidu', 
  'top=10,left=10,width=400,height=200');

在这里插入图片描述
下面我们来看一些常用参数(图片来自mdn,由于不同浏览器的页面结构不一样,因此不是每个参数对每个浏览器都生效,具体请以实际效果为准。在Chrome中,基本只支持left、top、height和width,因此我们暂且仅介绍这四个参数):

left,新窗口相对于当前浏览器页面左侧的距离。
top,新窗口相对于浏览器页面顶部的位置,注意,不是相对于文档区域,而是整个浏览器页面。 height,窗口内容区(即用户区,不包含工具栏、标签栏等)的高度,单位像素,最小值100。 width,窗口的宽度(包含滚动条),单位像素,最小值100。
其他参数如menubar、toolbar、location、personalbar等在Chrome中均不支持,如果需要在其他浏览器中启用,请参考mdn - window.open

2. 返回值

window.open返回的是对新打开的窗口的引用,即该窗口的window对象:

let refWin = window.open('https://www.baidu.com', 'baidu');
console.log(refWin);

不过这里引用到的window对象并不具备完整的DOM属性和方法,它仅仅提供了访问该页面的一些基本属性和方法,如图所示。 在这里插入图片描述

blur(),手动移除窗口焦点的方法,refWin.blur()可使该窗口失去焦点。
close(),关闭该窗口的方法。
closed,标识该窗口是否已经被关闭。
frames,新窗口内的frames。
length,新窗口内iframes的数量。
location,新窗口window的location对象,用于访问窗口的地址信息。
opener,该窗口的打开者。如我们在a页面通过window.open打开b页面,那么b页面的window.opener就是a页面的window。
parent,该窗口的父窗口,由于是顶级窗口,因此它的值等于window自身。
postMessage,通信接口,通过该方法可以实现向新窗口发送消息,优势是支持跨域。
self、window、top,前两个均代指当前window,top指的是当前窗口所在页面的顶级窗口,由于自身已经是顶级窗口,因此top也是当前window。

3. 通信问题

使用window.open打开新窗口时,原窗口与新窗口之间是可以实现双向通信的。

在不跨域的情况下,可以直接访问页面内的任何全局变量、方法或DOM元素等。新窗口通过window.opener可以找到原窗口的window
原窗口可以直接通过window.open的返回值访问新窗口,或者通过该窗口的名字找到该窗口,方法为:let ref = window.open('', 'windowName')。

由于新窗口的加载是异步的,因此不能在调用window.open之后立即访问该窗口。可以在窗口的onload事件内访问新窗口:

let ref = window.open('/index.html');
ref.onload = function(){
  ... // 与新窗口通信
}

而在新页面中,可以直接通过window.opener访问原窗口,如:

// 查找原窗口内的p元素
let p = document.querySelectorAll('p', 
  window.opener.document);

在跨域的情况下以上的方法会报错,因为会受到浏览器跨域安全策略的限制,此时就需要通过window.postMessage实现页面之间的通信。

在原窗口,可以通过对新窗口的引用调用postMessage:

let ref = window.open('https://www.baidu.com');
ref.postMessage(data, '*');

在新窗口内,同样通过window.opener访问原窗口:

window.opener.postMessage(data, '*');

在使用postMessage进行通信的时候存在一个小的兼容性问题,那就是IE8和IE9中的独立窗口之间通信时只能传递字符串,不支持其他数据类型,而在与页面内的iframe通信时没有这个问题

总结 window.open本质上可以看做<a>标签的js版本,或者说是编码式地打开窗口。
但它比<a>标签更加灵活,可以通过js实现与打开的页面之间的通信。

实践

父页面-Vue引入iframe:  直接通过添加iframe标签,src属性绑定data中的src。

<template>
  <div class="act-form">
    <iframe :src="src" ref="iframe"></iframe>
    <div @click="sendMessage">向iframe发送信息</div>
  </div>
</template>
<script>
export default {
  data () {
    return {
      src: '子页面src地址',
      iframeWin: {}
    }
  },
  methods: {
    sendMessage () {
      // 外部vue向iframe内部传数据
      this.iframeWin.postMessage({
        cmd: 'getFormJson',
        params: {
            action:'read', // 'read','write','update' ...等
            source:'父页面的origin地址', // 可以传父页面的origin地址给 子页面
            ...
        }
      }, '*') // '*' 可以改成 子页面的origin地址
    }
  },
  mounted () {
    // 在父页面(外部vue)的window上添加postMessage的监听,并且绑定处理函数handleMessage
    // 父页面监听子页面传来的值
    window.addEventListener('message', event => {
    	 // 根据上面制定的结构来解析 子页面-iframe内部发回来的数据
	    const data = event.data
	    switch (data.cmd) {
	      case 'returnFormJson':
	        // 业务逻辑
	        break
	      case 'returnHeight':
	        // 业务逻辑
	        break
	    }
    });
    this.iframeWin = this.$refs.iframe.contentWindow
  }
}
</script>

子页面向父页面传值,iframe内向外部vue发送信息:

<!DOCTYPE html>
<html lang="en">
	<head>
	    <meta charset="utf-8">
	    <title>iframe Window</title>
	    <style>
	        body {
	            background-color: #D53C2F;
	            color: white;
	        }
	    </style>
	</head>
	<body>
	    <h1>Hello there, i'm an iframe</h1>
	    <script>
	        // 子页面向父vue页面发送信息
	        window.parent.postMessage({
	            cmd: 'returnHeight',
	            params: {
                      action:'read', // 'read','write','update' ...等
                      source:'',// 子页面地址origin
	              success: true,
	              data: document.body.scrollHeight + 'px'
	            }
	        }, '*'); // '*' 可以改成 父页面的origin地址
	        // 接受父页面发来的信息
	        window.addEventListener("message", function(event){
	          var data = event.data;
	          switch (data.cmd) {
	            case 'getFormJson':
	                // 处理业务逻辑
	                break;
	            }
	        });
	    </script>
	</body>
</html>

参考文章:blog.csdn.net/qq_41694291…