访问和操作浏览器窗口的模型称为浏览器对象模型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); // 29console.info(window.getAge()); // 29console.info(this == window); // trueconsole.info(this == self); // trueconsole.info(window == self); // truewindow.age = '23';//直接在window对象上定义属性,会同时修改全局变量的值,相反,修改全局变量的值,window对象的属性值也会修改console.info(age);delete window.age;//既定义了全局变量,又在window对象上定义了属性,删除时虽然没有报错,但是并没有起作用console.info(age); //23console.info(window.age); //23window.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作为顶层对象的主要方法
说明:
A、表示窗口位置的属性有screenLeft、screenTop、screenX、screenY、表示窗口大小的属性有innerWidth、innerHeight、outerWidth、outerHeight,但是它们的具体含义和不同的浏览器密切相关,在上表中没有列入。这些属性的差异性给窗口控制的兼容性造成了非常大的困扰,不过好在现在已经有很多成熟的JS框架,在处理浏览器差异性建议使用这些JS框架。
B、moveTo()、moveBy()、resizeTo()、resizeBy()四个方法不适用于框架,只适用于最外层的window对象,而且这些方法可能会被浏览器禁用(在Opera和IE7+中默认就是禁用的)。
C、window.open()方法中的第三个参数为特性字符串,是一个逗号分隔的设置字符串,用来表示在新窗口中都显示哪些特性,整个特性字符串不允许出现空格,特性和特性以逗号分隔,特性和特性值以等号(=)间隔。这些特性有(蓝色的为默认值):
D、window.open()方法返回新创建窗口的一个引用,可以利用这个引用来操作新窗口,新窗口对象中有一个opener属性指向原窗口。
//创建新窗口,返回这个新窗口对象的引用,如果代码是在新打开窗口的页面,则可以直接通过window访问var openWin = window.open("/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对象的主要属性和方法有:
三、location对象
location对象提供了与当前窗口中加载的文档有关的信息以及一些导航功能,它既是window对象的属性,同时也是document对象的属性,它的主要属性和方法有:
说明: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);