《JavaScript高级程序设计》第八章,BOM。
BOM是浏览器对象模型,它提供了很多对象,用于访问浏览器的功能。很久以来都缺少对BOM的规范,各个浏览器提供商都会自行随意拓展它,不过现在的HTML5规范已经包含BOM的主要方面了。
window对象
window对象是BOM的核心,表示浏览器的一个实例。它既是通过JavaScript访问浏览器窗口的一个接口,又是ECMAScript规定的Global对象。
全局作用域
window是ECMAScript的Global对象,因此所有全局作用域中声明的变量、函数都会变成window对象的属性和方法。不过全局变量和在window对象上直接定义属性也有一定的区别:全局变量不能通过delete操作符删除,其上直接定义的属性可以。原因在于使用var语句添加的window属性的[[Configurable]]被设置为false,不可通通过delete删除。
1 | var age = 29; |
还有一个区别是访问未声明的变量会抛出错误,但可通过访问window属性的语法来判断一个变量是否存在,这样不会抛出错误。
1 | var newVar = oldVar; // 出错,因为oldVar未定义 |
窗口关系及框架
若页面中包含框架则每个框架都有自己的window对象,并且存在frames集合中,每个window对象都有自己存储框架名称的name属性。因此可以通过window.frames[i]
或window.frames["frameName"]
来访问框架。除此之外还可以使用top、parent、self这三个window对象来访问,top指向最外层框架即浏览器窗口,parent对象指向当前框架的直接上层框架,self指向window。所有这些对象也都是window对象的属性,因此可以通过window.top
和window.parent
等访问。下图展示了在最高层窗口访问每个框架的模式。
1 | <html> |
* 不同框架间的问题
使用框架时,浏览器中会存在多个Global对象,每个框架中的window对象都包含对原生类型的构造函数,但它们并不相等。如top.Objec和top.frames[0].Object不相等,这会影响到对跨框架传递的对象使用instanceof操作符。
* frameset已在HTML5中被废弃
窗口定位
- screenLeft、screenTop、screenX、screenY
都表示窗口相对于屏幕左边和上边的位置,前两者用于IE、Safari、Opera、Chrome,后两者用于Firefox。但是不同浏览器对于该值的定义也不太一样,不赘述了。
1 | var leftPos = (typeof window.screenLeft == "number") ? |
- moveTo()、moveBy()
两个方法可以将窗口精确地移动到一个新位置,均接收两个参数。moveTo()
参数为新位置的x和y,moveBy()
参数为在水平和垂直方向上移动的像素数。不过这两个方法可能被浏览器禁用,且都不适用于框架,只适用于最外层的window对象。
1 | window.moveTo(0, 0); // 窗口移动到(0,0) |
窗口大小
- innerWidth、innerHeight、outerWidth、outerHeight
前两者表示视口(viewport)大小,后两者表示浏览器窗口大小,具体因浏览器而异。还可以使用DOM中的一些操作返回页面相关信息:标准模式下的document.documentElement.clientWidth
和document.documentElement.clientHeight
;混杂模式下的document.body.clientWidth
和document.body.clientHeight
。
- resizeTo()、resizeBy()
两个方法可以调整浏览器窗口的大小,均接收两个参数。resizeTo()
参数为新宽度和新高度,resizeBy()
参数为新窗口与原窗口的宽度和高度之差。不过这两个方法可能被浏览器禁用,且都不适用于框架,只适用于最外层的window对象。
导航和打开窗口
window.open()
方法可以导航到特定URL或打开新的浏览器窗口,可以接收4个参数:URL、窗口目标、特性字符串、表示新页面是否取代当前页面的布尔值(通常只传递第一个参数),返回指向新窗口的引用。window.close()
方法可以关闭新打开的窗口。
第二个参数
若第二个参数不是已存在的窗口或框架,就会根据第三个参数来创建一个新窗口或新标签页。
1 | // 等同于<a href="http://www.baidu.com" target="topFrame"></a> |
这里相当于用户单击了href属性为"http://www.baidu.com"
,target属性为”topFrame”的链接。如果有一个名为”topFrame”的窗口或框架,就会在该窗口或框架下加载这个URL;否则就会创建一个新的窗口并将其命名为”topFrame”。第二个参数也可以是特殊的窗口名称:_self、_parent、_top、_blank。
第三个参数
若没有传入第三个参数,就会打开一个带有全部默认设置(工具栏、地址栏、状态栏等)的新浏览器窗口或标签页。第三个参数是以逗号分隔的设置字符串,表示新窗口中有哪些特性,整个特性字符串中不允许出现空格,可选项如下。
值为yes或no:
- fullscreen:窗口最大化
- location:显示地址栏
- menubar:显示菜单栏
- resizable:可拖动边框改变大小
- scrollbars:允许滚动
- status:显示状态栏
- toolbar:显示工具栏
值为数值:
- left
- top
- width
- height
1 | window.open("http://www.baidu.com", "baiduWindow", |
这里打开了一个可以调整大小的新窗口,初始大小为400*400像素,距屏幕上沿和左边各10像素。
通信
新建的window对象有opener属性,保存着打开它的原始窗口对象。
1 | var baiduWin = window.open("http://www.baidu.com", "baiduWindow", |
安全限制
一些广告商会将弹出的窗口打扮成系统对话框,欺骗引诱用户去点击它们的广告。为了解决这个问题,有些浏览器开始在弹出窗口配置方面增加限制:如不允许在屏幕之外创建弹出窗口、不允许将弹出窗口移动端屏幕之外、不允许关闭地址栏等等。
后来浏览器有了内置的弹出窗口屏蔽程序,或者可以安装带有屏蔽程序的工具。前者发生的时候,window.open()
会返回null,后者发生的时候,window.open()
会抛出一个错误。因此可以用以下代码准确地检测弹出窗口是否被屏蔽:
1 | var blocked = false; |
超时调用和间歇调用
JavaScript是单线程语言,但它允许通过设置超时值和间歇时间值来调度代码在特定时刻执行。
超时调用
超时调用是setTimeout()
,接收两个参数:要执行的代码和以毫秒表示的时间。前者可以是包含JavaScript代码的字符串或函数,但由于传递字符串可能导致性能损失,不建议使用字符串。
1 | // 1s后alert |
JavaScript是个单线程的解释器,因此一定时间内只能执行一段代码。为了控制要执行的代码,有一个JavaScript任务队列。而setTimeout()
的第二个参数就是在告诉JavaScript再过多长时间把该任务添加到队列中。如果队列为空则被添加的代码立刻执行,否则要等前面的代码执行后再执行。因此,指定的代码不一定会准确地在指定的时间后执行。setTimeout()
返回一个数值id,它是计划执行代码的唯一标识符。可通过该标识符来取消超时调用,使用clearTimeout()
。
1 | var timeoutId = setTimeout(function() { |
间歇调用
间歇调用是setInterval()
,接收的参数与上相同,返回值也是id。它会按照指定的时间间隔重复执行代码,直至间歇调用被取消或页面被卸载。
想要在某刻停止间歇调用可以用以下两种方式:
1 | var num = 0; |
实践中很少使用间歇调用,且更建议使用方法二的用超时调用模拟间歇调用。
系统对话框
浏览器通过alert()
、confirm()
、prompt()
来调用系统对话框向用户显示消息。系统对话框与在浏览器中显示的网页无关,也不含HTML、CSS等,样式由浏览器决定。这三个方法打开的对话框都是同步和模态的,即显示对话框的时候代码会停止执行,关闭对话框后代码又恢复执行。alert()
的结果是一个警告对话框,包含OK(确定)按钮;confirm()
的结果是一个确认对话框,包含OK(确定)按钮和Cancel(取消)按钮,返回一个布尔值。
1 | if(confirm("Are you sure?")) { |
prompt()
的结果是一个提示框,用于提示用户输入一些文本,包含文本输入框、OK(确定)按钮、Cancel(取消)按钮。它接收两个参数:提示和文本输入区的默认值。没有点击OK的情况下会返回null。
1 | var result = prompt("What's your name?", ""); |
Chrome还引入了一种新特性:如果执行过程中会打开两个和以上的对话框,那么从第二个对话框开始会多带一个复选框,勾选它就可以阻止后续对话框的显示。
除了上述三种,还有通过JavaScript打开的对话框:查找和打印。这两个对话框都是异步显示的,能够将控制权立即交还给脚本。
1 | // 显示查找对话框 |
location对象
location对象提供与当前窗口加载的文档有关的信息,也能将URL解析为独立的片段供开发人员使用,它既是window对象的属性也是document对象的属性,即window.location和doocument.location引用的是同一个对象。location对象有以下属性:
- hash:URL的hash(#后跟零或多个字符),如
"#contents"
- host:服务器名和可能存在的端口号
- hostname:服务器名
- href:完整URL
- pathname:URL中目录或文件名
- port:端口号或空字符串
- protocol:协议,如
"http:"
- search:查询字符串,如
"?q=javascript"
查询的字符串参数
可以如下进行查询字符串的解析:
1 | function getQueryStringArgs() { |
位置操作
可以有许多方法改变浏览器的位置:
1 | location.assign("http://www.wrox.com/WileyCDA"); |
每次修改location除hash外的属性,页面都会以新URL重新加载。通过上述任何一种方法修改URL后都会在浏览器历史记录中生成一条新记录,因此用户可以点击回退按钮导航至前一页面。若想禁用该行为可使用replace()
,它接收一个URL参数,可以在不生成历史记录的情况下导航到相应URL,也因此禁用了回退按钮。
1 | // 每隔1s重新定向至"http://www.wrox.com"且不能回退 |
若想重新加载页面可以使用reload()
。当不接收参数时,页面会以最有效的方法重新加载,如没变的页面直接从缓存中加载;当接收参数时,该参数应为true,表示前置从服务器重新加载。
1 | location.reload(); // 可能从缓存加载 |
navigator对象
navigator对象有很多属性:
- appCodeName:浏览器名称
- appVerson:浏览器版本
- cookieEnabled:cookie是否启用
- …
检测插件
非IE浏览器检测插件可以使用navigator的plugins数值,该数组中每一项都包含如下属性:插件名name、插件描述description、插件文件名filename、插件所处理的MIME类型数量length。
1 | function hasPlugin(name) { |
* 插件对象类型
为MimeType对象的数组。每个MimeType都有4个属性:包含MIME类型描述的description、回指插件对象的enabledPlugin、表示与MIME类型对应的文件拓展名的字符串suffixes、表示完整MIME类型的type。
IE浏览器因不支持Netscape式插件而需要使用专有的ActiveXObject类型,并尝试创建一个特定插件的实例。IE中插件是用COM对象实现的,用唯一标识符来标识,如Flash的标识符为ShockwaveFlash.ShockwaveFlash。
1 | function hasPlugin(name) { |
注册处理程序
HTML5定义了registerContentHandler()
和registerProtocolHandler()
,俩方法可以让一个站点指明它可以处理特定类型的信息。均接收3个参数,前者为RSS源的MIME类型、应接收RSS源URL的URL、应用程序名称;后者为要处理的协议、处理该协议页面的URL、应用程序名称。
1 | // 将一个站点注册为处理RSS源的处理程序 |
screen对象
这个对象用来表明客户端的能力,用处不大。举例几个属性:
- availHeight:屏幕的像素高度减系统部件高度的值
- bufferDepth:读、写用于呈现屏外位图的位数
- logicalXDPI:屏幕逻辑的水平DPI
- …
history对象
history对象保存用户上网的历史记录。history是window对象的属性,因此每个窗口、标签页、框架都有自己的history对象。出于安全方面的考虑,开发人员不能得知用户浏览过的URL。有几个比较常用的方法:go()
、back()
、forward()
。
1 | history.go(-1); // 后退一页 |
history还有个length属性,保存着历史记录的数量。对于加载到窗口、标签页或框架中的第一个页面而言,length为0。
* 我的笔记越写越没有灵魂了TAT