第十一章介绍了window对象及其客户端javascript所扮演的核心角色:它是客户端javascript程序的全局对象。本章介绍window对象的属性和方法,这些属性定义了不同的API,但是只有一部分实际上和浏览器窗口相关。window对象是以窗口命名的。
1节展示如何使用setTimeout()和setInterval()来注册一个函数,并在指定的时间后调用它。
2节讲述如何使用location属性来获取当前显式文档的URL和载入新的文档
3节介绍history属性,并展示如何在历史记录中向前和向后移动
4节展示如何使用navigator获取浏览器的厂商和版本信息,如何使用screen属性来查询窗口的尺寸
5节展示如何使用alert()、prompt()、confirm()来显示简单的文本对话框,以及如何使用showModalDialog()显示HTML对话框
6节讲解如何使用onerror处理方法,这个方法在未补货javascript异常发生时调用
7节讲述HTML元素的ID和name作为window对象的属性来使用
8是一个很长的节,讲解如何打开和关闭浏览器。以及如何编写在多个窗口和嵌套窗体中的javascript 代码
1.计时器
setTimeout()和setIterval()可以用来注册在指定的时间之后,单词或重复调用的函数。因为它们都是客户端javascript中的重要全局函数,所以定义为window对象方法,但作为通用函数,其实并不会对窗口做什么事情。
window对象的setTimeout()方法用来实现一个函数在指定的毫秒后运行。setTimeout返回一个值,这个值可以传给clearTimeout()用于取消这个函数的执行。
SetInterval()和setTimeout()一样,只不过这个函数会在指定的毫秒数的间隔里重复调用:
setInterval(updataClock,60000);//,每隔60秒调用一次updataClock函数
和setTimeout()一样,setInterval()函数也返回一个值,这个值可以传递给clearInterval(),用于取消后续函数的调用。
下面的例子定义的函数会在指定的时间之后,开始重复调用某个函数,然后在过了一段时间后取消函数的调用。该例子演示了setTimeout、setInterval、clearInterval的用法
/**定时器应用函数 * 安排函数f()在未来的调用模式 * 在等待了若干时间后调用f() * 如果设置了interval并没有设置end参数,则f()的调用不会停止 * 只有制定了f(),才会从start=0时刻开始 * 注意,调用invoke()不会阻塞,会立即返回 * */ function invoke(f, start, interval, end) { if (!start) start = 0;//默认设置为0 if(arguments.length <=2)//单次调用模式 setTimeout(f,start);//start秒后调用f else{ setTimeout(repeat,start);//若干秒后调用f() function repeat(){ var h = setInterval(f,interval);//循环调用f() //在end结束后调用,嵌套是end已经定义了 if(end) setTimeout(function(){clearInterval(h);},end); } } }
一个简单的定时器函数
var Obj = function(msg) { this.msg = msg; this.shout = function() { alert(this.msg); } this.waitAndShout = function() { var owner = this; setTimeout(function() { owner.shout.call(owner); }, 2000); } } var aa = new Obj("abc"); aa.waitAndShout();
由于历史原因,setTimeout()和setInterval()第一个参数可以作为字符串传入。如果这么做,那这个字符串会在指定的超时时间或间隔之后求值(相当于执行eval()).除了前两个参数之外,HTML5规范还允许setTimeout()和setInterval()传入额外的参数,并且在调用函数的时候把这些参数传过去。
如果以0毫秒时间调用setTimeout,那么指定的函数不会立刻执行。相反会把它放到队里中,等到前面处于等待状态的事件处理程序完全完成之后,再立刻调用它。
2.浏览器的定位和导航
window对象的location属性引用的是Location对象,它表示该窗口中当前文档的URL,并定义了方法使创口引用到Location对象。
window.location === document.location; //=>true 总是返回true
Document对象也有一个URL属性,是文档首次载人后保存该文档的URL静态字符串。如果定位到文档中的片段标识符(如#table-of-contens),Location对象会做响应更新,而document.url属性却不会改变。
i.解析url
window对象的location属性引用是Location对象,表示当前显示的的文档的URL。Location对象的href属性是一个字符串。Location对象的toString()方法返回href属性的值,因此会在隐式调用toString()情况下,使用location代替Location.href
var d = location.href; var c = location.toString(); console.log(d === c)
这个对象的其它属性,protocol,host,hostname,port,pathname和search分别表示URL的各个部分。它们称为"URL分解",同时被Link对象(通过HTML文档中的a和<area>元素创建)支持,参阅本书的第四部分,Location和Link项获取详细信息。
Location对象的hash和seerch比较有意思。hash表示URL中的“片段标识符 #xx”部分,search返回问好之后的URL,这部分一般是某种类型的查询字符串。
下面的例子展示了一个通用函数urlArgs()定义,可以用这个函数将参数从URL的search属性中提取出来。该例子用到了decodeURIComponent(),后者是在客户端Javascript定义的全局函数(参加本书第三章部分中的Global获取详细内容)。
/** *这个函数来解析来自URL的查询中串中的name=value参数 * 它将name=value对象存储在一个对象的属性中,并返回该对象,这样使用它 * * var args = urlArgs();//从URL中解析参数 * var q = args.q ||"";//如果参数定义的话就使用它,否则使用默认值。 * var n = args.n?parseInt(args.n):10 **/ function urlArgs() { var args = {}; //空对象 var query = location.search.substring(1); //?后的值 var pairs = query.split("&"); for (var i = 0; i < pairs.length; i++) { //对于每个片段 var pos = pairs[i].indexOf('='); if (pos == -1) continue; //如果没有找到的话,跳过 var name = pairs[i].substring(0, pos); //提取name var value = pairs[i].substring(pos + 1); //提取value value = decodeURIComponent(value); //对value进行解码 args[name] = value; } return args; }
ii.载入新的文档
Location对象的assign()方法可以使窗口载入并显示你指定的url文档。replace()方法也类似,但它在载入新的文档之前会从浏览历史把当前文档删除。如果脚本无条件的载入一个新的文档,replace()方法是可能比assgin()方法更好的选择。否则,后退 按钮会使用浏览器带回原始文档。而相同的脚本则会再次载入新文档。如果检测到用户浏览器不支持某些特性来显示功能齐全的版本,可以这样做:
//如果浏览器不支持XMLHttpRequest对象 //则将其重定向到一个不需要Ajax的静态页面 if (XMLHttpRequest) location.replace("aa.html");
除了assign()和replace()方法,Location对象还定义了reload()方法,后者可以让浏览器重新载入当前文档。
传统的方法一般是这样做
location = "http://a.com";
location = "page.html";
纯粹的片段标识符也是url的一种类型,可以这样定义
location = "http://a.com#cll";
location = "page.html#top";
Location对象的URL是可写的,对它们重新赋值会改变URL的位置,导致浏览器载入新的文档(如果是hash属性,则定位到片段标识符)
location.search = "?page=" + (pagename+1);//载入新的页面
3.浏览历史
window对象的history属性是引用该窗口的History对象。History对象是用来把窗口的浏览历史用文档和文档状态列表的形式表示。History对象的length属性表示浏览意思列表的数量元素,处于安全考虑,脚本不能访问已经保存的URL.
history对象的back()和forward()方法与浏览器的“后退”和“前进”按钮一样。它们可以往前后跳转一格,第三个方法go(),接受一个参数,可以像前(正参数)向后(幅参数)跳过任意个页面。
history.go(-2); //向后两个历史记录,相当于后退按钮两次
如果窗口包含多个子窗口(比如iframe见本文8.ii节),子窗口的历史会按照穿插主窗口的历史中。
现代的web应用可以不通过载入新的文档而动态的改变自身内容。这么做希望用户能用“后退”和“前进”按钮在这些动态创建应用状态之间跳转。html5将这些技术标准(18章2节)。
在现实的工作中,那些需要以前的HTML5历史管理项目中,开发者会使用一些现成 解决方法(因为之前历史管理是个复杂的难题),很多框架都实现了这些功能。例如jQuery的history插件。也有一些单独的类库。RSH(Really Simple History)是其中一个比较流行的示例。(20章2节会有关于html5的历史管理)
4.浏览器和屏幕信息
本节介绍window对象的navigator和screen属性,它们分别引用的是Navigator和Screen对象,这些对象信息允许脚本根据环境定制的行为。
浏览器有一定的嗅探价值。这样的一种情况是,当需要解决某个浏览器特定的bug时,Navugator对象有4个属性提供关于运行中的浏览器的版本信息。并可以使用这些属性就行嗅探。
i.Nagvigator
appName
web浏览器的全称,IE中,它显示“Microsoft Internet Exploer”.在firefox中,该属性就是“Netscape”,在其它浏览器中也是(为了兼容现在的浏览器嗅探码)。
appVersion
此属性通常以数字开始,并跟随浏览器开发商和版本信息等详细字符串。字符串的前边通常是4.0或者5.0,表示它是第4还是第5代兼容的浏览器。appVersion没有标准格式。所有没有办法判断浏览器的类型
userAgent
浏览器在它的User-Agent HTTP头部发送的字符串。这个属性通常包含所有appVersion信息,并且常常包含其它细节。和appVersion一样,它也没有标准格式。由于这个属性包含绝大部分信息,因此,浏览器嗅探代码通常使用它。
platform
在其上运行的浏览器的操作系统(可能是硬件的字符串)。
下面的例子展示了使用正则表达式(来自jQuery)从navigator.userAgent抽取浏览器的版本和名称方法代码。
/* 使用naVigator.userAgent来进行浏览器嗅探 */ //为客户端嗅探定义browser.name和browser.version,这里使用了jQuery1.41的代码 //name和number都是字符串,对浏览器的输出结果不一样,检测的结果如下 //'webkit':safari或chrome,版本号是WebKit的版本号 //'opera' Opear 版本号就是软件的把嫩好 //"mozilla" Firefox或者基于gecko内核的浏览器,版本号是Gecko的版本 //msie IE+版本号 var browser = (function() { var s = navigator.userAgent.toLowerCase(); var match = /(webkit)[ /]([w.]+)/.exec(s) || /(opera)(?:.*version)?[ /]([w.]+)/.exec(s) || /(msie)([w.]+)/.exec(s) || !/compatible/.test(s) && /(mozilla)( ? : .* ? rv : ([w.] + )) ? /.exec(s)||[]; return{name:match[1]||"",version:match[2]||"0"}; }());
另外一种
var info = {}; info.browser = function() { ua = navigator.userAgent.toLowerCase(); var rwebkit = /(webkit)[ /]([w.]+)/; var ropera = /(opera)(?:.*version)?[ /]([w.]+)/; var rmsie = /(msie) ([w.]+)/; var rmozilla = /(mozilla)(?:.*? rv:([w.]+))?/; var match = rwebkit.exec(ua) || ropera.exec(ua) || rmsie.exec(ua) || ua.indexOf("compatible") < 0 && rmozilla.exec(ua) || []; return { name: match[1] || "", version: match[2] || "0" }; }(); console.log(info.browser)
处理浏览器厂商和版本信息的属性之外,Navigator对象还包含一些杂项属性和方法。以下是一些标准化的属性以及广泛应用但未标准的属性
OnLine
navigator.onLine是表示是否浏览器是否连接到网络
geolocation
navigator.geolocation对象拥有确定用户地址位置信息的接口,参加20.1章节细节
javaEnabled
navigator.javaEnabled是一个非标准的方法,当浏览器能运行java小程序时返回true
cookieEnabled
navigator.cookieEnabled也是一个非标准方法,如果浏览器可以永久保存cookie时,返回true,而cookie配置“视具体情况而定”时,可能不返回正确的值。
ii.Screen对象。
window对象的screen属性。它提供有关窗口的大小和可用颜色的数量和信息。属性width和heigth指定是以小苏为单位的窗口大小,属性availWidth和availHeiht指是实际可用的显示大小。属性colorDepth是值显示的BPP值,典型的值有16,24,32
window.screen属性和它引用的screen对象都是非标准但广泛实现的。可用用screen对象来确定web是否运行在一个小的屏幕设备上,如果屏幕有限,是否要选用更小的字体和图片等。
5.对话框
window对象提供了三个方法向用户显示简单点对话框,alert(),confirm()要求用户单击确定或取消,返回一个布尔值,prompt()同样也显示一条消息,等待用户输入字符串,并返回那个字符串。下面的代码全用了这三种方法:
do { var name = prompt("你的名字"); var correct = confirm("你输入了" + name + " " + "oK确认,chancel取消"); } while (!correct) alert("hello" + name);
尽管alert(),confirm()和prompt()都容易使用,但是良好的设计都需要有节制的使用它们。如今,唯一常见的就是使用它们调试,查看变量输出的结果是什么。
注意,这些对话框中显示的文本是纯文本,只能使用空格,换行符和各种标点符号来格式化这些对话框。
confirm()和prompt()都会产生阻塞,也就是说再关掉它们所显示的对话框之前,它们不返回,也会停止载入,知道用户响应位置。大多数浏览器里,alert()也会产生阻塞,等待用户关闭关闭对话框,但不总是这样,完整的的细节请参考第四部分的Window.alert()/Window.confirm()、和window.prompt()方法。
除了window的alert()、confirm()和prompt()方法,还有更复杂的方法showModalDialog(),显示一个包含HTML格式的模态对话框(模态对话框就是弹出来后只能点击当前的对话框的内容,底下的页面不能操作)。当窗口关闭后,window.returnValue属性的值就是此方法的返回值。对话框的HTML内容往往必须包含用来设置returnValue的“确认”按钮,如果需要则调用window.close(12.8.i)
下面的例子是一个适用于showModalDialog()的html文件,代码顶部注释包含了showModalDialog()的样例。对话框里显示的大量文本都来自showModalDialog()第二个参数,而不是写死在html中。
<!-- /*使用showModalDialog()方法来显示出对话框*/ 这个html不是独立的,这个文件由showModalDialog()调用 它希望window.dialogArguments是一个由字符串组成的数组,数组的一个元素放在对话框的顶部,剩下的每个元素是每行的输入框的标识 当单击确定时,返回一个数组,这个数组是每个输入框的值组成 使用这样的代码开确定: var p = showModalDialog("multiprompt.html", ["请输入3d坐标","x","y","z"], "dialog400;dialogheight:300;resizable:yes"); --> <form> <fieldset id="fields"></fieldset> <!--对话框正文部分--> <div style="text-align: center;"> <button onclick="okay()">确定</button> <!--设置返回值和关闭事件--> <button onclick="cancel()">取消</button> <!--关闭时不带任何返回值--> </div> <script type="text/javascript"> //创建对话框主题部分,在fieldset中显示出来 var args = dialogArguments; var text = "<legend>" + args[0] + "</legend>"; for (var i = 1; i < args.length; i++) text += "<label>" + args[i] + ":<input id = 'f" + i + "'></label><br>"; document.getElementById("fields").innerHTML = text; //直击关闭这个对话框,不设置返回值 function cancel() { window.close(); } //读取输入框的值,然后设置一个返回值,之后关闭 function okay() { window.returnValue = []; //返回一个数组 for (var i = 1; i < args.length; i++) //设置输入框的元素 window.returnValue[i - 1] = document.getElementById("f" + i).value; window.close(); //关闭对话框,使用showModealDialog()返回 } </script> <form>
6.错误处理
window对象对象的onerror属性是一个事件处理程序,当未捕获的异常传播到调用栈上时就会调用它,并把错误消息输出到浏览器的javascript控制台上。如果给这个属性赋值一个函数,那么只要这个窗口中发生了javascript错误,就会调用该函数,即它成为了窗口的错误处理程序。
由于历史原因,事件处理函数调用通过三个字符串参数,而不是通过传递一个事件对象。(其它客户端对象的onerror处理程序所需要的错误条件是不一样的,但是它们都是正常的事件处理程序,这个函数只需传入一个事件对象。)window.onerror的第一个参数是描述错误的一条消息。第二个参数是一个字符串,它存放引发错误的javascript代码所在文档的 url.第三个参数是引发错误的行数。
除了这三个参数外,onerror处理程序的返回值也很重要。如果onerror处理程序返回false,它通知浏览器事件程序已经处理了错误,不需要其他操作。换句话说,浏览器不应该显示它自己的错误消息。由于历史原因,Firefox里的错误程序必须返回true表示它已经处理了错误
onerror处理程序是很早期的javascript产物,那时候语言核心不包含try/catch异常处理语句。现在的代码已经很少使用它。但是在开发阶段,你可能需要定义一个错误处理程序,当错误发生时,显式的通知自己。
window.onerror = function(msg, url, line) { alert("出错了: " + msg + " 地址:" + url + " 行号:" + line); return true; //屏蔽系统事件 }
7.作为window对象属性的文档元素
如果在HTML文档中使用id属性来为元素命名,并且如果window对象没有此名字的属性,window对象会赋予一个属性,它的名字是id属性的值,而他们指向表示文档元素的HTMLElement对象。
客户端javascript中,window对象是以全局对象的形式存在与作用域链上层,这就意味着在html文档中使用的id属性会成为被脚本访问的全局变量。如果一个文档包含<button id="okay"></button>元素,可以通过全局变量okay引用此元素。
但是:有一个很重要的警告!: 如果window对象已经具有此名字的属性 ,这就不会发生。比如id是history,location或navigator的元素,就不会以全局变量的形式出现。因为这些id已经被占用了。同样,如果html文档包含一个id为“x”的元素,并且还在胆码中声明并赋值给全局变量x,那么显式的声明的变量会隐藏隐式的元素变量。在脚本中的变量声明出现在命名元素之前,那这个变量的存在就会阻止元素获取它的window属性。而如果脚本中的变量声明出现在命名元素之后,那么变量的显式赋值就会覆盖该属性的隐式值。
13章2节中,你会通过document,getElementById()方法,用HTML的id属性来查找文档元素。见下面的例子:
var ui = ["input", "prompt", "heading"]; //数组中存放要查找的元素id ui.forEach(function(id) { //用每个id查找对于的元素 ui[id] = document.getElementById(id); //将其存放在一个属性中 });
运行完这段代码之后,ui.input,ui.prompt和ui.heading会引用文档元素。脚本可以用全局变量input和heading来代替ui.input和ui.heading
但记得12.5节里window对象有个方法的名字是prompt(),所用脚本中不能用全局变量prompt代替ui.prompt.
元素的id作为全局变量的隐式应用是web浏览器演化过程中遗留的怪癖。它主要是出于与已有web页面向后兼容性的考虑。但这里并不推荐这种做法---浏览器厂商可以在任何时候为window对象定义新属性,而这些新属性都会破坏使用了此属性名的隐式定义的代码。反之,用document.getElementById()来显式查找元素,如果给他一个更简单的名字,这种用法会更加简单:
var $ = function(id) { return document.getElementById(id); }; ui.prompt = $("prompt")
很多客户端类库都定义了$函数,类似上面一样通过id来查找元素。(17章内为jQuery的$函数为通用的元素选择方法,基于id,标签名,class属性或其它标准,返回一个或多个元素)
假设ID并没有被window对象使用的话,那么任何有id属性的html元素都会变成全局变量的值。以下HTML元素如果有name属性的话,也会这样表现:
<a> <applet> <area> <embed> <form> <frame> <frameset> <img> <object>
id元素在文档中必须是唯一的,两个元素不能有相同的id。但是这对name属性无效。如果上面的元素有多于一个相同的name属性(或者一个元素有name属性,而另外一个元素有相同值的id属性),具有该名称的隐式全局变量会引用一个类数组对象,这个类数组对象的元素是所有命名的元素。
有name或id属性的<iframe>元素是个特殊的例子。为它们隐式创建的变量不会引用表示元素自身的Element对象,而是引用表示<iframe>元素创建的嵌套的window对象。本文8.ii会讨论到它。
8.多窗口和窗体
一个web页面窗口可能在桌面上包含多个标签页。每一个标签 页都是独立的“浏览上下文”(browsing context),每一个上下文都有独立的window对象,而且相互之间互不干扰。每个标签页中运行的脚本通常并不知道其它标签页的存在,更不用说和其他标签也的window对象进行交互操作或者操作其文档内容了。如果web浏览器不支持多标签页或者把标签页关掉了,可能某一刻桌面上有很多打开的浏览器窗口。而使用标签页,每个窗口中的window对象都是独立的,也就是说彼此就是完全独立的和其它桌面窗口没有关系。
但是窗口不总是和其它窗口完全没有关系。一个窗口或标签页中的脚本可以打开新的窗口或标签页,当一个脚本这样做时,多个窗口或窗口与另一个窗口文档之间就可以互操作。(11.6.ii讲解的同源策略约束)。本节i小节会介绍窗口的打开和关闭的更多内容。
html文档经常使用<iframe>来嵌套多个文档。由它所创建的嵌套浏览上下文是用它自己的window对象所表示的。废弃的<frameset>和<frame>元素同样创建了嵌套浏览的上下文。每一个<frame>都有独立的window对象表示。对于客户端javascript来说,窗口、标签页、iframe和框架都是独立的浏览上下文。对于javascript来说,它们都是window对象。和相互独立的标签页不同,嵌套的浏览上下文之间并不是相互独立的。在一个窗体中运行javascript程序总是能看见它的祖先和子孙窗体,尽管脚本查看这些窗体中的文档受到同源策略限制。
因为window是客户端javascript的全局对象,每个窗口或窗体都包含堵路的javascript执行上下文。不过在一个窗口中的javascript代码,如有同源策略的限制,则可以使用另外一个窗口中定义的对象、属性和方法。与此相关的细节会在本节iii节详细讨论。由于同源策略的限制导致窗口之间无缝直接交互时,html5提供了一个机遇消息传输的API,可以用于间接通信。20章3节会讨论。
i.打开和关闭窗口
使用window对象的open()方法可以打开一个新的浏览器窗口(或标签页,这通常和浏览器的配置项有关)。window.open()载入指定的url到新的活已存在的窗口中,并返回那个窗口的window对象。它有四个可选的参数。
open()第一个参数是要在新窗口打开文档的url。如果省略(也可以是空字符串),那么会使用页面的URLabout:blank
opne()第二个参数是新打开窗口的名。如果指定的是一个以及存在的窗口的名字(并且脚本允许跳转到那个窗口),会直接使用已经存在的窗口。否则,会打开新的窗口,并将这个指定的名字赋值给它。如果省略此参数,会使用指定的名字“_blank”打开新的、为命名的窗口。
需要注意的是,脚本是无法通过简单的猜测窗口的名字来控制操纵这个窗口中的web应用的,只有设置了“允许导航”(allowed to navigte)(html5规范术语)的页面才可以这样。宽泛的讲,当且仅当窗口包含的文档来自相同的源或者是这个脚本打开了那个窗口(或者是递归地打开了窗口中打开的窗口),脚本才可以通过名字来指定存在的窗口。还有,如果其中一个窗口是内嵌在另一个窗口的里的窗体,那么它们的脚本直接就可以相互导航。在这种情况下,可以使用保留的名字“_top”(顶级祖先窗口)和“_parent”(直接父级窗口)来获取彼此浏览上下文。
窗口名字 窗口的名字是非常重要的,因为它允许open()方法引用已存在的窗口,并同时可以作为<a>和<form>元素上的html target属性值,用户表示引用的文档(或表单提交结果)应该显示在命名窗口中,这个target属性值可以设置为"_blank"."_parent"或"_top",从而使引用的文档显示在新的空白窗口、父窗口/窗体 或顶层窗口中。 window对象如果有name属性,就用它保存名字。该属性是可写的,并且脚本可以随意设置它,如果传递为window.open()脚本一个除"_blank"之外的名字,通过该调用创建的窗口将以改名字作为那么属性的初始值。如果<iframe>元素有name属性,表示该iframe的window对象会用它作为 name属性的初始值。
open()的第三个可选参数是一个以逗号分隔的列表,包含大小和各种属性,用以表明新窗口是如何打开的。如果省略它,新窗口会用一个默认的大小,而且带有一整组标准的UI组件,即菜单栏/状态栏、工具栏等。在标签式浏览器中,会创建一个新的标签
另外一方面,如果指定这个参数,就可以指定窗口的尺寸,以及它包含的一组属性。(显式指定窗口支持更像是创建新窗口,而不是新标签)例如,允许打开改变大小的浏览器窗口,并且包含状态栏、工具栏,地址栏可以这样写
var w =window.open("smaillwin.html","smallwin","width=400,heigth=500,status=yes,resizeable=yes");
第三个参数是非标准的,html5规范也主张浏览器应该忽略它。参见 第四部分中的window.open()查看在此参数中可以指定什么内容。注意,当指定第三个参数时,所有没有显式指定的功能都会忽略。除以各种原因,浏览器包含可能对可能指定功能的限制。例如:通常不允许指定一个太小或者位于屏幕之外才窗口,并且浏览器不允许创建一个没有状态栏的窗口。
open()的第四个参数只在第二个参数命名的是一个存在的窗口时才有用。它是一个布尔值,声明了由第一个参数是url是应用替换掉窗口浏览历史的当前条目(true),还是应该在窗口历史条目创建一个新的条目false,后者为默认设置。
open()的返回值是代表命名或新创建窗口的window对象。可以在自己的javascript代码中使用这个window对象来引用新创建的窗口,就像使用隐式的window对象window来引用运行代码的窗口一样
var w = window.open(); w.alert("to example.com"); w.location = "http://www.example.com"
在window.open()方法创建窗口中,opener属性引用的是打开它脚本的window对象。在其它窗口总,opener为null:
w.opner !== true; //true, 对于任何由w创建的任意窗口 w.open().opener === w; //true,对于任意窗口w
window.open()是广告商采用在你“浏览窗口之前弹出”或“页面之后弹出”窗口的一种方法。由于这种方法的滥用,大部分浏览器增加了弹出窗口过滤系统,通常,open()方法只有当用户单击按钮或者链接的时候才会调用,javascript代码尝试在浏览器载入或关闭时开启弹出一个窗口时,通常会失败。将上面的代码粘贴到浏览器javascript控制台进行测试 ,可能会因为同样的原因而失败。
关闭窗口
就像方法open()打开一个新窗口一样,方法close()将关闭一个窗口。如果已经创建了window对象w,可以使用如下的代码将它关闭。
w.close()
运行在那个窗口中的中的关闭方法可以用javascript将其关闭。如下
window.close();
注意,要显式地使用标识符window。这样可以避免混淆window对象的close()方法和Document对象的close()方法,如果正在从事的事件程序调用close()。这很重要
大多浏览器只允许自动关闭由自己的javascript代码创建的窗口。如果要关闭其他窗口,可以用一个对话框提示用户,对要求关闭的窗口进行确认或取消。在表示窗体而不是顶级窗口或标签页上的window对象执行close()方法不会任何效果,它不能关闭一个窗体(反之可以从它包含的文档中删除iframe)。
即使一个窗口关闭了,代表它的window对象仍然存在。已关闭的窗口会有个值为为true的closed属性。它的Document会是null。它的方法通常再也不会工作。
ii.窗体之间的关系
我们已经知道,window对象的方法open()返回代表创建窗口的window对象,而且这个新窗口具有opener属性,该属性可以打开它的原始窗口。这样,两个窗口就可以相互引用,彼此都可以互相读取对方的属性或是调用对方的方法。窗体也是这样的,窗口或窗体中运行的代码都可以通过下面介绍的属性引用到自己才窗口或窗体,以及嵌套的子窗体。
任何窗口或窗体中的javascript代码都可以将自己的窗口和窗体引用位window或self.窗体可以用parent属性引用包含它的窗口或窗体的window对象。
parent.history.back();
如果一个窗口是顶层窗口或标签,而不是窗体,那么气parent属性引用的就是这个窗口本身。
parent == self; //只有顶级窗口才会返回true
如果一个窗体包含在另一个窗体中,而后者又包含在顶级窗口中,那么该窗体就可以使用parent.parent来引用顶级窗口。top属性是一个通用的快捷方式,无论一个窗体被嵌套了几层,它的top属性引用总是指向包含它的顶级窗口。如果一个window对象代表的是一个顶级窗口,那么它的top属性引用的就是窗口本身。对于那些顶级窗口的直接子窗口,top属性就等价于parent属性。
parent和top属性允许脚本引用它的窗体的祖先。有不止一种方法可以引用窗口或窗体的子孙窗体。窗体是iframe元素创建的。可以用 获取其他元素的一个方法获取一个表示iframe元素的对象。假定<iframe id="f1">,那么表示该iframe元素的对象就是:
var iframeElement = document.getElementById("f1");
<iframe>元素有contentWindow属性,引用该窗体的window对象,所以此窗体的window对象就是:
var childFrame = document.getElementById("f1").contentWindow;
可以进行反向操作——从表示窗体的window对象来获取该窗体的<iframe>元素,用window对象的frameElement属性。表示顶级窗口的window对象的frameElement属性为null。窗体中的window对象的frameElement属性不是null:
var elt = document.getElementById("f1"); var win = elt.contentWindow; win.frameElement === elt; //对于帧来说,永远是true window.frameElement === null; //对于顶级窗口来说,永远是true
尽管如此,通常不需要使用getElementById()和contentWindow属性来获取窗口中的子窗体的引用,每个window对象都有一个frames属性,它引用自身包含的窗口或窗体的子窗体。frames是继续引用是类数组对象 ,并可以通过数字或窗体名进行索引。引用窗口中的第一个子窗体,可以用frames[0]。引用第二个子窗体的第三个子窗体,可以用frames[1].frames[2],窗体里运行的代码可以用parent.frames[1]引用兄弟窗体。注意frames[]数组里的元素是window对象,不是<iframe>元素。
如果指定<iframe>元素的name或id属性,那么除了用数字进行索引之外,还可以用名字来索引 .例如名字为f1的帧应该用frames["f1"]或frames.f1。
本章第7小节中,<iframe>以及其他元素的name和id都可以自动通过window对象的属性来引用,而<iframe>元素和其它元素有所不同:对于窗体来说,通过window对象的属性引用的<iframe>是指窗体中的window对象,而不是元素对象。也就是说,可以通过窗体的名字f1来代替frames.f1。实际上,HTML5规范指出,frames属性是一个自引用(self-referential)的属性,就像window和self一样。而这个window对象看起来像一个窗体组成的数组。也就是说,可以window[0]来获取第一个窗体的引用,可以通过window.length或length查询窗体的编号。但是这里我们使用frames代替window会更清晰些,尽管这种方法有些传统。需要注意的是,当前的浏览器不会让frame==window,在frame和window不相等的情况下,可以通过子窗口的索引或名字来获得其他对象的引用。
可以使用<iframe>的元素的name或id属性作为javascript代码中的引用表示。但如果使用name属性的话,所指定的name同样会代表这个窗体的window对象的name属性。以这种方式给出的名字可以做一个链接的target属性,而且它可以用作window.open()的第二个参数。
iii.交互窗口中的javascript
每个窗口和窗体都是它自身的javascript执行上下文,以及window作为全局对象。但是如果一个窗口或窗体中的代码可以应用到其他窗口或窗体(并且同源策略没有阻止它),那么一个窗口或窗体中的脚本就可以和其它窗口或窗体的脚本进行交互。
设想一个web页面中有两个<iframe>元素 ,分别叫A和B,并且这些窗体所包含的文档来自同一服务器,并且包含交互脚本,窗体A里的脚本定义了一个变量i
var i = 3;
这个变量只是全局对象的一个属性,也是window对象的一个属性。窗体A中的代码可以用标识符i来引用变量,或者用window对象显式的引用这个变量:
window.i
由于窗体B中的脚本可以引用窗体A的window对象,因此它可以引用那个window对象的属性:
parent.A.i=4;//改变窗体A中的变量i的值
我们知道,定义函数的function可以声明一个变量,就像关键字var所做的那样。如果B脚本什么了一个(非嵌套)的函数f,这个函数在窗体B中是全局变量,并且在窗体B中可以用f()调用f,但是窗体A中的代码必须将f作为窗体B的window对象的属性f来调用
parent.B.f();//调用窗体B中定义的一个函数
如果窗体A中需要很频繁的调用这个函数,则可以将这个函数赋值给窗体A中的一个变量,这样就可以经常使用这个变量来引用窗体中的函数了:
var f = parent.B.f;
现在窗体A中的代码就可以像窗体B中的代码一样调用函数f()了。
采用这种方式在窗体或窗口间共享函数时,牢记词法的作用域规则非常重要。函数在定义它的作用域中执行,而不是在它的作用域中执行。就上面的那个例子来说,如果函数f引用引用了全局变量,那么将在窗体B中查找这些变量,即使函数是有窗体A调用的。
要记住构造函数也是函数,所有当用构造函数和相关的原型对象定义一个类(9章)时,那个类只能在单独的窗口中定义。假设set.js中的窗口包含窗体A和窗体B,并且包含Set类
顶级窗体中定义的脚本可以创建新的Set对象,类似这样:
var s = new Set();
相反,每个窗体中的代码辨析显式地用父级窗口中的属性来引用Set()构造函数:
var s = new parent.Set();
另外,每个窗体中的代码必须显式地用父级窗口的属性来引用Set()构造函数
var set = top.Set(); var s =new Set();
和用户定义的类不同,内置的类(比如String,Date和RegExp)都会在所有的窗口汇总自动预定义。但是要注意,每个窗口都有构造函数的一个独立副本和构造函数对于袁旭对象的一个独立副本。例如,每个窗口都有定义的String字符串的新方法,并且通过把它赋值给当前窗口中的String.prototype对象而使它成为String类的一个方法,那么该窗口中的所有字符串就都可以使用这个新方法。但是,别的窗口中定义的字符串不能使用这个新方法。
事实上每个window对象都有自己的原型对象,这意味这instanceof操作符不能跨窗口工作。例如,当instanceof来标记窗体B的一个字符串和窗体A的string()构造函数时,结果会为false。7章10介绍了决定跨窗口数组的类型时的相关困难。
WindowProxy对象
我们已经讲过很多次,Window对象是客户端javascript的全局变量。但是从技术来看,并不是这样的。web浏览器每次向窗口或窗体中载入新的内容,它都会开始一个新的javascript对象执行上下文,包含一个新创建的全局对象。但是当多个窗口或窗体在使用时,有一个很重要的概念,尽管窗体或窗口载入 新的文档,但是引用窗体或窗口的window对象还仍然是一个有效的引用。
所有客户端javascript有两个重要的对象。客户端全局对象处于作用域链的顶级,并且是全局变量和函数所定义的地方。事实上全局对象会在窗口或窗体载入新内容时被替换,我们成为“window”的对象实际上不是全局对象,而是全局对象的一个代理。每次当查询或设置window对象的属性时,就会在窗口或窗体当前全局对象上查询或设置相同的属性。HTML5将这个规范称代理对象为WindowProxy
由于它的代理行为,除了有更长的生命周期外,代理对象表现得像真正的全局对象。如果可以比较这两个对象,那么区分它们会很困难。但事实上,没有办法可以用到正真的客户端全局对象。全局对象处于作用域的顶端,但是window、self、parent以及窗体中的属性全部返回代理对象。window.open()方法也返回代理对象。甚至顶级函数里的this关键字都是代理对象,而不是真正的全局对象。
(本文完,您可以关注上一章:第十一章:WEB浏览器中的javascript,将更新:第十三章:校本化文档。欢迎关注)