多窗口和窗体
窗口指的是浏览器中的窗口或标签页,而窗体指的是由 <iframe>
所引入的子窗口。
打开和关闭窗口
打开窗口
使用 Window 对象的 open()
方法可以打开一个新的浏览器窗口(或标签页,这与浏览器的配置有关),Window.open()
载入指定的 URL 到新的或已存在的窗口中,并返回代表那个窗口的 Window 对象,该方法有4个可选参数:
- 第一个参数是要在窗口中显示的文档的URL,如果这个参数为空,则打开一个
about:blank
空页面 - 第二个参数是新打开的窗口的名字,如果是已存在的窗口名则直接使用已存在的窗口,否则会打开新窗口,并将指定的名字赋给它,如果此参数为空,会使用
_blank
打开一个新的、未命名的窗口(Window 对象如果有 name 属性则用它作为窗口名,iframe 亦然) - 第三个参数是一个逗号分隔的列表,包含窗口大小和各种属性,用以表明窗口是怎么打开的,如果该参数为空,新窗口将使用默认的大小,而且带有一整组标准的 UI 组件
- 第四个参数只在第二个参数命名的是一个存在的窗口时才有用,它是一个布尔值,声明了由第一个参数指定的 URL 是应用替换掉浏览历史的当前条目(true),还是应该在窗口浏览历史中创建一个新的条目(false),默认是false
open()
的返回值是代表命名或新创建的窗口的 Window 对象,可以在自己的 JavaScript 代码中使用这个 Window 对象来引用新创建的窗口:
var w = window.open('http://laravelacademy.org', 'laravelacademy', 'width=400,height=350,status=yes,resizable=yes', false);
w.alert('准备访问 http://laravelacademy.org');
w.location = 'http://laravelacademy.org';
在由 window.open()
方法创建的窗口中,opener
属性引用的是打开它的脚本的 Window 对象,而在其它窗口中,该属性值是 null
。
为了避免广告商滥用该方法弹出广告窗口,大部分浏览器都增加了弹出窗口过滤系统,只有用户手动单击按钮或链接时才会调用这个方法,JavaScript 代码尝试在文档载入时开启开启一个弹出窗口,通常会失败。
关闭窗口
就像 open()
方法打开一个新窗口一样,close()
方法将关闭一个窗口。对于已创建的 Window 对象 w
,可以通过如下代码将其关闭:
w.close();
在运行窗口中关闭当前窗口,则可以使用如下代码:
window.close();
注意,这里要显式使用标识符 close()
,避免混淆 Window 对象的 close()
方法和 Document 对象的 close()
方法。
在表示窗体(iframe
)而不是顶级窗口或标签页上的 Window 对象上执行 close() 方法不会有任何效果,它不能关闭一个窗体(可以从文档中删除 iframe
元素)。
即使一个窗口已经关闭了,代表它的 Window 对象仍然存在,已关闭的窗口会有个值为 true
的 closed
属性,它的 document
会是 null
,它的方法通常也不再工作。
窗体之间的关系
我们已经知道,Window 对象的 open()
方法返回代表它创建的新窗口的 Window 对象,而且这个新窗口有 opener
属性,该属性可以打开创建它的原始窗口。这样,两个窗口之间就可以相互引用,彼此都可以读取对方的属性或调用对方的方法,窗体也是如此。
任何窗口或窗体中的 JavaScript 代码都可以将自己的窗口和窗体引用为 window
或 self
,窗体可以用 parent 属性引用包含它的窗口或窗体的 Window 对象:
parent.history.back();
如果一个窗口是顶级窗口或标签,而不是窗体,那么其 parent 属性引用的就是这个窗口本身:
parent === self;
top
属性是一个通用的快捷方式,无论一个窗体被嵌套几层,它的 top
属性引用的都是指向包含它的顶级窗口,同理,如果 Window 对象代表的是一个顶级窗口,那么它的 top
属性引用的也是窗口本身。
有多种方法可以获取窗口或窗体的子孙窗体,<iframe>
元素有 contentWindow
属性,引用该窗体的 Window 对象,所以可以这样获取窗体的 Window 对象:
var childFrame = document.getElementById("f1").contentWindow;
此外,每个 Window 对象都有一个 frames
属性,它引用自身包含的窗口或窗体的子窗体,frames
属性引用的是类数组对象,可以通过数字或窗体名进行索引,数组元素是 Window 对象,而不是 <iframe>
元素。
交互窗口中的 JavaScript
每个窗口和窗体都是它自身的 JavaScript 执行上下文,以 Window 对象作为全局对象,但是如果一个窗口或窗体中的代码可以应用到其他窗口或窗体,那么一个窗口或窗体中的脚本就可以和其他窗口或窗体中的脚本进行交互。
设想一个 Web 页面里有两个 <iframe>
元素,分别叫「A」和「B」,并假设这些窗体所包含的文档来自于相同的一个服务器,并且包含交互脚本。窗体 A 里的脚本定义了一个变量 i
:
var i = 3;
这个变量只是全局对象的一个属性,也是 Window 对象的一个属性。窗体 A 中的代码可以用标识符来引用变量,或者用 window
对象显式地引用这个变量:
window.i
由于窗体 B 中的脚本可以引用窗体 A 的 Window 对象,因此它也可以引用那个 Window 对象的属性:
parent.A.i=4;//改变窗体A中的变量i的值
我们知道,定义函数的关键字 function
可以声明一个变量,就像关键字var
所做的那样。如果窗体 B 中的脚本声明了一个(非嵌套的)函数 f
,这个函数在窗体 B 中是全局变量,并且窗体 B 中的代码可以用 f()
调用它。但是窗体 A 中的代码必须将 f
作为窗体 B 的 Window 对象的属性来引用:
parent.B.f();//调用窗体B中定义的一个函数
如果窗体 A 中的代码需要很频繁地使用这个函数,则可以将这个函数赋值给窗体A中的一个变量,这样就可以经常使用这个变量来引用窗体中的函数了:
var f = parent.B.f;
现在窗体 A 中的代码就可以像窗体 B 中的代码那样调用函数 f()
了。当采用这种方式在窗体或窗口间共享函数时,牢记词法作用域的规则非常重要:函数在定义它的作用域中执行,而不是在调用它的作用域中执行。就上面那个例子来说,如果函数 f
引用了全局变量,那么将在窗体 B 的属性中查找这些变量,即使函数是由窗体 A 调用的。
要记住构造函数也是函数,所以当用构造函数和相关的原型对象定义一个类时,那个类只在一个单独的窗口中定义。假设顶级窗口包含窗体 A 和窗体 B,并且包含 Set
类。顶级窗口中的脚本可以创建新的 Set
对象,类似这样:
var s = new Set();
相反,每个窗体中的代码必须显式地用父级窗口的属性来引用 Set()
构造函数:
var s = new parent.Set();
另外,每个窗体中的代码还可以定义自己的变量来引用构造函数,这样就更方便了:
var Set = top.Set();
var s = new Set();
和用户定义的类不同,内置的类(比如 String、Date 和 RegExp)都会在所有的窗口中自动预定义。但是要注意,每个窗口都有构造函数的一个独立副本和构造函数对应原型对象的一个独立副本。例如,每个窗口都有自己的 String()
构造函数和 String.prototype
对象的副本。因此,如果编写一个操作 Javascript 字符串的新方法,并且通过把它赋值给当前窗口中的 String. prototype
对象而使它成为 String 类的一个方法,那么该窗口中的所有字符串就都可以使用这个新方法。但是,别的窗口中定义的字符串不能使用这个新方法。
事实上,毎个 Window 都有自己的原型对象,这意味着 instanceof
操作符不能跨窗口工作。例如,当用 instanceof
来比较窗体 B 的一个字符串和窗体 A 的 String()
构造函数时,结果会为 fa1se
。
No Comments