访问和操作浏览器窗口的模型称为浏览器对象模型BOM(Browser Object Model),但习惯上是把所有针对浏览器的JavaScript扩展都纳入BOM的范畴。BOM提供了一组独立于网页内容而与浏览器交互的对象,但由于缺乏相关的行业规范,各浏览器提供商在很长一段时间内都是按照各自的想法去实现这些对象的,因而浏览器之间共有的对象也就成为了事实上的标准,这种局面在HTML5出现后有望得到改观——HTML5致力于把很多BOM功能写入正式规范。由于不同浏览器之间的差异性,这里总结的主要是一些我认为比较常用的共有的特性,至于开发过程中的兼容性考虑,就交给流行的JS库吧(比如jQuery、ExtJS)。
先看一下下面的这个BOM对象图,从整体上对BOM有个认识:
上图中的window、document和location对象都具有双重身份:window对象既是ECMAScript规范中的Global对象,也是BOM中的顶级对象;document对象既是BOM顶级对象的一个属性,也是DOM模型中的顶级对象;location对象既是window对象的属性,同时也是document对象的属性。在BOM中,核心是window对象,下面就重点总结一下这个对象,document对象将在学习DOM模型时再做说明。
一、window对象
1、window作为ECMAScript中的Global对象
(1)引用Global对象的属性和方法时可以省略对象名,因此在引用window对象的属性和方法时,也可以省略window。
(2)在全局作用域中this和window指向同一个对象,另外,还可以使用self来引用window对象,也即有this===window===self。
(3)在全局作用域中定义的变量和函数也会成为window对象的属性和方法,但是和直接在window对象上定义属性还是有区别:
A、全局变量不能使用delete删除(相当于给window定义属性时将属性特性[[Configurable]]赋值为false了),但是直接在window对象上定义的属性可以使用delete删除。这里有趣的是,如果同时定义了全局变量和window对象的属性,则删除window属性时不起作用。
B、尝试访问未定义的全局变量会抛出异常,但是访问未定义的window对象的属性则只是返回undefined。
var age = '29';//全局作用域中定义的变量和函数会成为window对象的属性和方法,但是不能使用delete删除 function getAge(){ return this.age; } console.info(window.age); // 29 console.info(window.getAge()); // 29 console.info(this == window); // true console.info(this == self); // true console.info(window == self); // true window.age = '23';//直接在window对象上定义属性,会同时修改全局变量的值,相反,修改全局变量的值,window对象的属性值也会修改 console.info(age); delete window.age;//既定义了全局变量,又在window对象上定义了属性,删除时虽然没有报错,但是并没有起作用 console.info(age); //23 console.info(window.age); //23 window.color = 'red';//直接在window对象上定义属性,可以使用delete删除 console.info(window.color); delete window.color; console.info(window.color);//undefined
2、window作为BOM中的顶级对象
(1)窗口关系及框架
先来看看在BOM中几个具有特殊含义的对象top、window、parent、self:
- top:指向最高(最外)层的框架,也就是浏览器窗口。
- window:指向当前框架的顶层对象。
- parent:指向当前框架的直接上层框架,在没有子框架的情况下,parent、window、top和self都相等,在有子框架的情况下,parent有可能等于top,也有不能不相等。
- self:始终指向window,引入self的目的只是为了与top和parent对象对应起来。
如果页面中包含框架,则每个框架都有自己的window对象,并且保存在父窗口的frames集合中,可以通过索引(从0开始,从左至右,从上至下)或者框架名称访问相应的window对象。每个window对象都有一个name属性,表示所处框架的名称。下面是一个包括框架的页面:
<html> <head> <title>Frameset Example</title> </head> <frameset rows="160,*"> <frame src="frame.htm" name="topFrame"> <frameset cols="50%,50%"> <frame src="anotherframe.htm" name="leftFrame"> <frame src="yetanotherframe.htm" name="rightFrame"> </frameset> </frameset> </html>
在最外层的页面中,可以通过下面的方式来访问其中的框架:
(2)window作为顶层对象的主要方法
属性和方法 | 返回值 | 参数 | 说明 | ||
位置方法 | moveTo(x,y) | x和y表示新位置的x和y坐标值 | |||
moveBy(x,y) | x和y表示在水平和垂直方向上移动的像素数 | ||||
大小方法 | resizeTo(x,y) | x和y表示浏览器窗口的新宽度和新高度 | |||
resizeBy(x,y) | x和y表示浏览器窗口的新宽度、新高度和原宽度、原高度的差 | ||||
对话框 | 同步 | alert() | 提示文本 | 显示时包含传入的字符串和“确定”按钮 | |
confirm() | 点击“确认”返回true,点击“取消”返回false | 确认文本 | 显示时包含传入的字符串、“确认”按钮(返回true)和“取消”按钮(返回false),常用于确认删除操作 | ||
prompt() | 点击“确认”返回文本输入内容,点击“取消”返回null | 提示文本、文本输入域 | 显示时包含文本提示、“确认”按钮(返回文本输入域的内容)、“取消”按钮(返回null)和文本输入域。 | ||
异步 | find() | 相当于使用浏览器菜单栏的“查找”命令打开对话框 | |||
print() | 相当于使用浏览器菜单栏的“打印”命令打开对话框 | ||||
调度 | 超时 | setTimeOut() | 返回一个数值ID表示这个调用 | 执行函数或代码、执行代码前需要等待的时间(毫秒) | JavaScript是单线程的,超时调用和循环调用中的时间表示将需要执行的函数或代码加入到执行队列的时间,而不是实际执行的时间,加入到执行队列之后一有资源就会马上执行。由于这个原因,setInterval()可能存在一些间隔会被跳过执行,也可能间隔时间比预期的小,因此不建议使用循环调用,而采用下面的链式超时调用模式:
setTimeout(function(){ //处理代码 setTimeout(arguments.callee, time); }, time); |
clearTimeOut() | 根据ID取消超时调用 | 超时调用ID | |||
循环 | setInterval() | 返回一个数值ID表示这个调用 | 执行函数或代码、循环执行代码的间隔时间(毫秒) | ||
clearInterval() | 根据ID取消循环调用 | 循环调用ID | |||
新窗口 | 普通 | open() | 返回新打开窗口的引用 | 4个参数:目标URL,目标窗口名称,特性字符串,是否取代历史记录中当前加载页面的Boolean值 |
通常只需传递第一个参数,最后一个参数只在不打开新窗口的情况下使用。 如果传递了第二个参数,而且该参数是已有窗口或框架的名称,那么就会在相应的窗口或框架中加载目标URL,如果是一个不存在的名称,就会创建一个新窗口。第二个参数也可以是下面的特殊值:_self、_parent、_top、_blank。 |
说明:
A、表示窗口位置的属性有screenLeft、screenTop、screenX、screenY、表示窗口大小的属性有innerWidth、innerHeight、outerWidth、outerHeight,但是它们的具体含义和不同的浏览器密切相关,在上表中没有列入。这些属性的差异性给窗口控制的兼容性造成了非常大的困扰,不过好在现在已经有很多成熟的JS框架,在处理浏览器差异性建议使用这些JS框架。
B、moveTo()、moveBy()、resizeTo()、resizeBy()四个方法不适用于框架,只适用于最外层的window对象,而且这些方法可能会被浏览器禁用(在Opera和IE7+中默认就是禁用的)。
C、window.open()方法中的第三个参数为特性字符串,是一个逗号分隔的设置字符串,用来表示在新窗口中都显示哪些特性,整个特性字符串不允许出现空格,特性和特性以逗号分隔,特性和特性值以等号(=)间隔。这些特性有(蓝色的为默认值):
特性 | 取值范围 | 说明 |
fullscreen | yes|no | 表示浏览器窗口是否全屏,仅限IE |
height | 数值(不能小于100) | 新窗口的高度 |
width | 数值(不能小于100) | 新窗口的宽度 |
left | 数值(不能是负值) | 新窗口的左坐标 |
top | 数值(不能是负值) | 新窗口的上坐标 |
toolbar | yes|no | 是否在浏览器窗口中显示工具栏 |
status | yes|no | 是否在浏览器窗口中显示状态栏 |
scrollbars | yes|no | 如果内容在视口中显示不下,是否显示滚动条 |
resizable | yes|no | 是否可以通过拖动浏览器窗口的边框来改变其大小 |
menubar | yes|no | 是否在浏览器窗口中显示菜单栏 |
location | yes|no | 是否在浏览器窗口中显示地址栏,不同浏览器默认值不同,如果设置为no,地址栏可能会隐藏,也可能会被禁用 |
D、window.open()方法返回新创建窗口的一个引用,可以利用这个引用来操作新窗口,新窗口对象中有一个opener属性指向原窗口。
//创建新窗口,返回这个新窗口对象的引用,如果代码是在新打开窗口的页面,则可以直接通过window访问 var openWin = window.open("http://www.cnblogs.com/linjisong","cnblogs-lin","height=400,width=320,resizable=yes"); openWin.resizeTo(800,600);//重新定义大小 openWin.moveTo(100,100);//移动位置 console.info(openWin.opener === window);//true,新窗口对象中有一个opener属性指向原窗口 openWin.close();//关闭新窗口 console.info(openWin.closed);//true,关闭之后可以访问关闭状态
在使用window.open()方法时,出于安全性考虑,很多浏览器会有一些限制,比如Firefox强制显示地址栏等,在实际开发过程中,需要根据目标浏览器来测试这些限制并采取相应的措施。
E、在IE中,除了window.open()之外,还可以通过window.showModelDialog()来弹出一个窗口,通常称之为“模态窗口”,这种窗口会独占系统资源,用户只有在关闭它之后才能继续其它的操作。由于模态窗口不是兼容所有浏览器,这里不再展开,有兴趣的朋友可以找相应的资料了解一下。
二、history对象
history对象保存着从窗口被打开起的历史记录,每个浏览器窗口、标签页、框架都有自己的history对象。history对象的主要属性和方法有:
属性/方法 | 例子 | 说明 |
length | if(history.length == 0) //第一个页面 | 历史记录的数量 |
go() |
history.go(-1); history.go(0); history.go(2); history.go('linjisong'); |
负数表示向后跳转 0表示刷新当前页面 正数表示向前跳转 字符串参数,表示跳转到历史记录中包含该字符串的最近一个位置(可能前进,也可能后退) |
back() | history.back();相当于history.go(-1); | 后退一页,可模仿浏览器“后退”按钮 |
forward() | history.forward();相当于history.go(1); | 前进一页,可模仿浏览器“前进”按钮 |
三、location对象
location对象提供了与当前窗口中加载的文档有关的信息以及一些导航功能,它既是window对象的属性,同时也是document对象的属性,它的主要属性和方法有:
属性/方法 | 例子 | 说明 |
hash | "#contents" | 返回URL中的hash(#号后跟零或多个字符),如果URL中不包括散列,返回空字符串 |
host | "www.wrox.com:80" | 返回服务器名称和端口号(如果有) |
hostname | "www.wrox.com" | 返回不带端口号的服务器名称 |
href | "http://www.wrox.com" | 返回当前加载页面的完整URL,location对象的toString()方法也返回这个值 |
pathname | "/WileyCDA" | 返回URL中的目录和文件名 |
port | "8080" | 返回URL中指定的端口号,如果不含端口号,返回空字符串 |
protocol | "http:" | 反应页面使用的协议,通常是http:或https: |
search | "?q=javascript" | 返回URL的查询字符串,这个字符串以问号开头 |
assign() | location.assign(URL) | 立即打开新URL并在浏览器历史中生成一条记录,相当于直接设置location.href值,也可以修改location对象的其它属性来重新加载 |
replace() | location.replace(URL) | 打开新URL,但是不会生成历史记录,使用replace()之后,用户不能通过“后退”回到前一个页面 |
reload() | location.reload([true]) | 重新加载当前页面,不传递参数时会以最有效方式加载(可能从缓存中加载),传入true时,则强制从浏览器重新加载 |
说明:location.search返回的是包括所有参数的字符串,可以通过下面的方法将这个字符串转换为一个参数对象(原书第207页):
function getQueryStringArgs(){ var qs = (location.search.length > 0 ? location.search.substring(1) : ""),//去掉查询字符串前面的问号 args = {}, items = qs.length ? qs.split("&") : [],//将参数以&分隔 item = null, name = null, value = null, i = 0, len = items.length; for (i=0; i < len; i++){//循环处理,将每一个参数的名称和值加入到参数对象中 item = items[i].split("="); name = decodeURIComponent(item[0]); value = decodeURIComponent(item[1]); if (name.length){ args[name] = value; } } return args; } var args = getQueryStringArgs();//假定查询字符串返回?q=javascript&num=10 console.info(args["q"]); //"javascript" console.info(args["num"]); //"10"
注意:该方法对于查询字符串中同一个参数多个值的情况(类似?val=1&val=2)处理会有问题。
四、navigator对象
navigator对象用来描述浏览器本身,包括浏览器的名称、版本、语言、系统平台、用户特性字符串等信息,但是各个浏览器及浏览器的不同版本之间对这个对象的实现也不尽相同。
在非IE浏览器中,可以使用navigator对象的plugins属性来检测插件的安装情况:
function hasPlugin(name){ name = name.toLowerCase(); for (var i=0; i < navigator.mimeTypes.length; i++){ if (navigator.mimeTypes[i].name.toLowerCase().indexOf(name) > -1){ return true; } } return false; }
也可以使用navigator对象的userAgent属性(用户代理字符串)来检测客户端对某种功能的支持情况。不过在检测浏览器是否支持某种功能的时候,首先应使用特性检测来判断,特性检测的目标不是判断是什么浏览器,而是判断浏览器是否支持某种能力,比如在IE早期版本中不存在document.getElementById(),而是使用非标准的document.all来获取DOM元素,就可以使用如下代码:
function getElement(id){ if(document.getElementById){//特性检测,如果支持,优先使用document.getElementById() return document.getElementById(id); }else { return document.all[id]; } }
有时候虽然支持某种特性,但是这种特性并不是想要的功能,这种情况下,可以对特性进一步做检测,比如通过下面的代码进一步判断支持的特性是否为函数:
typeof document.createElement === 'function'
当然,特性检测并不总是能凑效,这时不得已就只好通过navigator对象来对检测了,因为userAgent是在浏览器发展过程中逐步演变而来的,因此使用这种检测技术需要了解各种浏览器的发展历史,这里就不具体展开了,建议就是使用成熟的JS库来代替直接使用navigator.userAgent进行检测。
五、screen对象
screen对象用来表明客户端的能力,包括浏览器窗口外部的显示器的信息,如像素宽度和高度等,每个浏览器中的screen对象都包含着各不相同的属性,其中五大浏览器都支持的属性有:
- height:屏幕的像素高度
- width:屏幕的像素宽度
- availHeight:屏幕的像素高度减系统部件高度之后的值(只读)
- availWidth:屏幕的像素宽度减系统部件宽度之后的值(只读)
screen对象经常集中出现在测定客户端能力的站点跟踪工具中,有时候也可能会用到其中的信息来调整浏览器窗口的大小,使其占据屏幕的可用空间,比如:
window.resizeTo(screen.availWidth, screen.availHeight);