BOM(browser object model)是浏览器对象模型的简称,被广泛应用于Web开发中,主要用于对客户端浏览器的管理。BOM的概念比较古老,但是一直没有被标准化,不过各主流浏览器均支持BOM,都遵守最基本的规则和用法,W3C也将BOM的主要内容纳入HTML5规范。
1、window对象
Window是客户端浏览器对象模型的基类,window对象是客户端JavaScript的全局对象。一个window对象实际上就是一个独立的窗口,对于框架页面来说,浏览器窗口中每个框架都包含一个window对象。
1.1、全局作用域
在客户端浏览器中,window对象是访问BOM的接口,如引用document对象的document属性,引用自身的window和self属性等,同时window也为客户端JavaScript提供全局作用域。
【示例】由于window是全局对象,因此所有的全局变量都被解析为该对象的属性:
var a = "window.a"; //全局变量
function f(){ //全局函数
console.log(a);
}
console.log(window.a); //返回字符串“window.a”
window.f(); //返回字符串“window.a”
注意:使用delete运算符可以删除属性,但是不能够删除变量。
1.2、访问客户端对象
使用window对象可以访问客户端的其他对象,这种关系构成浏览器的对象模型,window对象代表根节点,每个对象说明如下:
- window:客户端JavaScript顶层对象。每当
<body>
或<frameset>
标签出现时,window对象就会被自动创建。 - navigator :包含客户端有关浏览器的信息。
- screen:包含客户端屏幕的信息。
- history:包含浏览器窗口访问过的URL信息。
- location:包含当前网页文档的URL信息。
- document :包含整个HTML文档,可被用来访问文档内容及其所有页面元素。
1.3、实现人机交互
window对象定义了3种人机交互的方法,主要方便对JavaScript代码进行测试。
- alert():确定提示框。由浏览器向用户弹出提示性信息,该方法包含一个可选的提示信息参数。如果没有指定参数,则弹出一个空的对话框。
- confirm():选择提示框。由浏览器向用户弹出提示性信息,弹出的对话框中包含两个按钮,分别表示“确定”和“取消”。如果单击“确定”按钮,则该方法将返回true;单击“取消”按钮,则返回false。confirm()方法包含一个可选的提示信息参数,如果没有指定参数,则弹出一个空的对话框。
- prompt():输入提示框。可以接收用户输入的信息,并返回输入的信息。prompt()方法包含一个可选的提示信息参数,如果没有指定参数,则弹出一个没有提示信息的输入文本对话框。
【示例】演示综合调用window对象定义的3种人机交互方法,设计一个人机交互的对话框:
var user = prompt("请输入你的用户名:");
if( ! ! user){ //把输入的信息转换为布尔值
var ok = confirm("你输入的用户名为:\n" + user + "\n请确认。"); //输入信息确认
if(ok){
alert("欢迎你:\n" + user );
}
else{ //重新输入信息
user = prompt("请重新输入你的用户名:");
alert("欢迎你:\n" + user );
}
}else { //提示输入信息
user = prompt("请输入你的用户名:");
}
这3种方法仅接收纯文本信息,忽略HTML字符串,只能使用空格、换行符和各种符号格式化提示对话框中的显示文本。提示,不同浏览器对于这3个对话框的显示效果略有不同。
注意:显示系统对话框的时候,JavaScript代码会停止执行,只有当关闭对话框之后,JavaScript代码才会恢复执行。因此,不建议在实战中使用这3种方法,其仅作为开发人员的内测工具。
1.4、打开窗口
使用window对象的open()方法,可以打开一个新窗口,用法如下:
window.open(URL,name,features,replace)
参数说明如下:
- URL:可选字符串,声明在新窗口中显示网页文档的URL。如果省略或者为空,则新窗口就不会显示任何文档。
- name:可选字符串,声明新窗口的名称。这个名称可以用作标记
<a>
和<form>
的target目标值。如果该参数指定了一个已经存在的窗口,那么open()方法就不再创建一个新窗口,而只是返回对指定窗口的引用,在这种情况下,features参数将被忽略。 - features:可选字符串,声明新窗口要显示的标准浏览器的特征。如果省略该参数,新窗口将具有所有标准特征。
- replace:可选的布尔值,规定了装载到窗口的URL是在窗口的浏览历史中创建一个新条目,还是替换浏览历史中的当前条目。
该方法返回值为新创建的window对象,使用它可以引用新创建的窗口。
新创建的window对象拥有一个opener属性,引用打开它的原始窗口对象。opener只在弹出窗口的最外层window对象(top)中定义,而且指向调用window.open()方法的窗口或框架。
【示例1】演示打开的窗口与原窗口之间的关系:
win=window.open(); //打开新的空白窗口
win.document.write("<h1>这是新打开的窗口</h1>"); //在新窗口中输出提示信息
win.focus(); //让原窗口获取焦点
win.opener.document.write("<h1>这是原来窗口</h1>"); //在原窗口中输出提示信息
console.log( win.opener == window); //检测window.opener属性值
使用window的close()方法可以关闭一个窗口。例如,关闭一个新创建的win窗口,可以使用下面方法实现:
win.close;
如果在打开窗口内部关闭自身窗口,则应该使用下面的方法:
window.close;
使用window.closed属性可以检测当前窗口是否关闭,如果关闭则返回true,否则返回false。
1.5、控制窗口
window对象定义了3组方法分别用来调整窗口位置、大小和滚动条的偏移位置:moveTo()、moveBy()、resizeTo()、resizeBy()、scrollTo()和scrollBy()。
这些方法都包含两个参数,分别表示x轴偏移值和y轴偏移值。包含To字符串的方法都是绝对的,也就是x和y是绝对位置、大小或滚动偏移。包含By字符串的方法都是相对的,也就是它们在窗口的当前位置、大小或滚动偏移上增加所指定的参数x和y的值。
方法moveTo()可以将窗口的左上角移动到指定的坐标,方法moveBy()可以将窗口上移、下移或者左移、右移指定数量的像素。方法resizeTo()和resizeBy()可以按照相对数量和绝对数量调整窗口的大小。
【示例】将当前浏览器窗口的大小重新设置为宽200px、高200px,然后生成一个任意数字随机定位窗口在屏幕中的显示位置:
window.onload = function(){
timer = window.setInterval("jump()", 1000);
}
function jump(){
window.resizeTo(200, 200)
x = Math.ceil(Math.random() * 1024)
y = Math.ceil(Math.random() * 760)
window.moveTo(x, y)
}
提示:window对象定义了focus()和blur()方法,用来控制窗口的显示焦点。调用focus()方法会请求系统将键盘焦点赋予窗口,调用blur()方法则会放弃键盘焦点。
2、navigator对象
navigator对象存储了与浏览器相关的基本信息,如名称、版本和系统等。通过window.navigator可以引用该对象,并利用它的属性读取客户端的基本信息。
2.1、浏览器检测方法
检测浏览器类型的方法有两种:特征检测法和字符串检测法。这两种方法都存在各自的优点与缺点,用户可以根据需要酌情选择。
1.特征检测法
特征检测法就是根据浏览器是否支持特定功能决定相应操作的方式。这是一种非精确判断法,却是最安全的检测方法。准确检测浏览器的类型和型号是一件很困难的事情,而且很容易存在误差。如果不关心浏览器的身份,仅仅在意浏览器的执行能力,那么使用特征检测法就完全可以满足需要。
【示例1】检测当前浏览器是否支持document.getElementsByName特性。如果支持,就使用该方法获取文档中的a元素;否则再检测是否支持document.getElementsByTagName特性。如果支持就使用该方法获取文档中的a元素:
if(document.getElementsByName){ //如果存在,则使用该方法获取a元素
var a = document.getElementsByName("a");
}
else if(document.getElementsByTagName){ //如果存在,则使用该方法获取a元素
var a = document.getElementsByTagName("a");
}
当使用一个对象、方法或属性时,先判断它是否存在。如果存在,则说明浏览器支持该对象、方法或属性,那么就可以放心使用。
2.字符串检测法
客户端浏览器每次发送HTTP请求时,都会附带一个user-agent(用户代理)字符串。对于Web开发人员来说,可以使用用户代理字符串检测浏览器类型。
【示例2】BOM在navigator对象中定义了userAgent属性,利用该属性可以捕获客户端user-agent字符串信息:
var s = window.navigator.userAgent;
//简写方法
var s = navigator.userAgent;
console.log(s);
//返回类似信息:Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0; .NET4.0E; .NET4.0C;
InfoPath.3; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729)
user-agent字符串包含Web浏览器的大量信息,如浏览器的名称和版本。
注意:对于不同浏览器来说,该字符串所包含的信息也不尽相同。随着浏览器版本的不断升级,返回的user-agent字符串的格式和信息也会不断变化。
2.2、检测浏览器类型和版本号
检测浏览器类型和版本比较容易,用户只需要根据不同浏览器类型匹配特殊信息即可。
【示例1】检测主流浏览器类型,包括IE、Opera、Safari、Chrome和Firefox:
var ua = navigator.userAgent.toLowerCase(); // 获取用户端信息
var info ={
ie : /msie/.test(ua) && !/opera/.test(ua), //匹配IE浏览器
op : /opera/.test(ua), //匹配Opera浏览器
sa : /version.*safari/.test(ua), //匹配Safari浏览器
ch : /chrome/.test(ua), //匹配Chrome浏览器
ff : /gecko/.test(ua) && !/webkit/.test(ua) //匹配Firefox浏览器
};
在脚本中调用该对象的属性,如果为true,说明为对应类型浏览器,否则就返回false:
(info.ie) && console.log("IE浏览器");
(info.op) && console.log("Opera浏览器");
(info.sa) && console.log("Safari浏览器");
(info.ff) && console.log("Firefox浏览器");
(info.ch) && console.log("Chrome浏览器");
【示例2】通过解析navigator对象的userAgent属性,可以获得浏览器的完整版本号。针对IE浏览器来说,它是在MSIE字符串后面带一个空格,然后跟随版本号及分号。因此,可以设计一个如下的函数获取IE的版本号:
//获取IE浏览器的版本号
//返回数值,显示IE的主版本号
function getIEVer(){
var ua = navigator.userAgent; //获取用户端信息
var b = ua.indexOf("MSIE "); //检测特殊字符串MSIE的位置
if(b < 0){
return 0;
}
return parseFloat(ua.substring(b + 5, ua.indexOf(";", b))); //截取版本号,并转换为数值
}
直接调用该函数即可获取当前IE浏览器的版本号:
console.log(getIEVer()); //返回类似数值:10
E浏览器的版本众多,一般可以使用大于某个数字的形式进行范围匹配,因为浏览器是向后兼容的,使用是否等于某个版本显然不能适应新版本的需要。
【示例3】利用同样的方法可以检测其他类型浏览器的版本号,下面的函数是检测Firefox浏览器的版本号:
function getFFVer(){
var ua = navigator.userAgent;
var b = ua.indexOf("Firefox/");
if(b < 0){
return 0;
}
return parseFloat(ua.substring(b + 8,ua.lastIndexOf("\.")));
}
console.log(getFFVer()); //返回类似数值:64
对于Opera等浏览器,可以使用navigator.userAgent属性获取版本号,只不过其用户端信息与IE有所不同,如Opera/9.02(Windows NT 5.1; U; en),根据这些格式可以获取其版本号。
注意:如果浏览器的某些对象或属性不能向后兼容,这种检测方法也容易产生问题。所以更稳妥的方法是采用特征检测法,而不是使用字符串检测法。
2.3、检测操作系统
navigator.userAgent返回值一般都会包含操作系统的基本信息,不过这些信息比较散乱,没有统一的规则。用户可以检测一些更为通用的信息,如检测是否为Windows系统,或者为Macintosh系统,而不去分辨操作系统的版本号。
例如,如果仅检测通用信息,那么所有Windows版本的操作系统都会包含“Win”字符串,所有Macintosh版本的操作系统都包含“Mac”字符串,所有UNIX版本的操作系统都包含“X11”字符串,而Linux操作系统会同时包含“X11”和“Linux”字符串。
【示例】通过下面的方法可以快速检测客户端信息中是否包含上述字符串:
['Win', 'Mac', 'X11', 'Linux'].forEach(function(t) {
( t === 'X11') ? t = 'Unix' : t; //处理Unix系统的字符串
navigator['is' + t] = function () { //为navigator对象扩展专用系统检测方法
return navigator.userAgent.indexOf(t) != - 1; //检测是否包含特定字符串
};
});
console.log( navigator.isWin()); //true
console.log( navigator.isMac()); //false
console.log( navigator.isLinux()); //false
console.log( navigator.isUnix()); //false
3、location对象
location对象存储与当前文档位置(URL)相关的信息,简单地说,就是存储网页地址字符串。使用window对象的location属性可以访问相关的信息。
http:// www.mysite.cn:80/news/index.asp?id=123&name= location#top
location对象定义8个属性,其中7个属性可以获取当前URL的各部分信息,另一个属性(href)包含完整的URL信息,详细说明如下表所示。为了便于更直观地理解,下表中各个属性将以URL示例信息为参考进行说明:
使用location对象,结合字符串方法可以抽取URL中查询字符串的参数值。
【示例】定义一个获取URL查询字符串参数值的通用函数,该函数能够抽取每个参数和参数值,并以名/值对的形式存储在对象中返回:
var queryString = function(){ //获取URL查询字符串参数值的通用函数
var q = location.search.substring(1); //获取查询字符串,如“id=123&name= location”
var a = q.split("&"); //以&符号为界把查询字符串劈开为数组
var o = {}; //定义一个临时对象
for( var i = 0; i <a.length; i++){ //遍历数组
var n = a[i].indexOf("="); //获取每个参数中的等号小标位置
if(n == -1) continue; //如果没有发现则跳到下一次循环继续操作
var v1 = a[i].substring(0, n); //截取等号前的参数名称
var v2 = a[i].substring(n+1); //截取等号后的参数值
o[v1] = unescape(v2); //以名/值对的形式存储在对象中
}
return o; //返回对象
}
然后调用该函数,即可获取URL中的查询字符串信息,并以对象形式读取它们的值。
var f1 = queryString(); //调用查询字符串函数
for(var i in f1){ //遍历返回对象,获取每个参数及其值
console.log(i + "=" + f1[i]);
}
如果当前页面的URL中没有查询字符串信息,用户可以在浏览器的地址栏中补加完整的查询字符串,如“?id=123&name=location”,再次刷新页面,即可显示查询的字符串信息。
location对象的属性都是可读可写的。例如,如果把一个含有URL的字符串赋给location对象或它的href属性,浏览器就会把新的URL所指的文档装载进来并显示出来:
location = "http:// www.mysite.cn/navi/"; //页面会自动跳转到对应的网页
location.href = "http:// www.mysite.cn/"; //页面会自动跳转到对应的网页
如果改变location.hash属性值,则页面会跳转到新的锚点(<a name="anchor">或<element id="anchor">)
,但页面不会重载。
除了设置location对象的href属性外,还可以修改部分URL信息,用户只需要给location对象的其他属性赋值即可。这时会创建一个新的URL,浏览器会将它装载并显示出来。
如果需要URL的其他信息,只能通过字符串处理方法截取。例如,如果要获取网页的名称,可以这样设计:
var p = location.pathname;
var n = p.substring(p.lastIndexOf("/")+1);
如果要获取文件扩展名,可以这样设计:
var c = p.substring(p.lastIndexOf(".")+1);
location对象定义了两种方法:reload()和replace():
- reload():可以重新装载当前文档。
- replace():可以装载一个新文档而无须为它创建一个新的历史记录。也就是说,在浏览器的历史列表中,新文档将替换当前文档,这样在浏览器中就不能够通过“返回”按钮返回当前文档。
对使用框架并且显示多个临时页的网站来说,replace()方法比较有用,这样临时页面都不被存储在历史列表中。
注意:window.location与document.location不同,前者引用location对象,后者只是一个只读字符串,与document.URL同义。但是,当存在服器重定向时,document.location包含的是已经装载的URL,而location.href包含的是原始请求文档的URL。
4、history对象
history对象存储客户端浏览器的浏览历史,通过window对象的history属性可以访问该对象。实际上,history对象仅存储最近访问的、有限条目的URL信息。
注意,在HTML5之前,为了保护客户端浏览信息的安全和隐私,history对象禁止JavaScript脚本直接操作访问信息。不过HTML5新增了一个History API,该API允许用户通过JavaScript管理浏览器的历史记录,实现无刷新更改浏览器地址栏的链接地址,配合History+Ajax可以设计不需要刷新页面的跳转。
在历史记录中后退,等效于在浏览器的工具栏上单击“返回”按钮:
window.history.back();1
在历史记录中前进,等效于在浏览器的工具栏上单击“前进”按钮:
window.history.forward();1
移动到指定的历史记录点。使用go()方法从当前会话的历史记录中加载页面。当前页面位置的索引值为0,上一页就是-1,下一页为1,以此类推:
window.history.go(-1); //相当于调用back()
window.history.go(1); //相当于调用forward()
使用length属性可以了解历史记录栈中一共有多少页:
var num = window.history.length;
1.添加和修改历史记录条目
HTML5新增history.pushState()和history.replaceState()方法,允许用户逐条添加和修改历史记录条目。使用history.pushState()方法可以改变referrer的值,而在调用该方法后创建的XMLHttpRequest对象会在HTTP请求中使用这个值。referrer的值则是创建XMLHttpRequest对象时所处的窗口的URL。
【示例】假设http://mysite.com/foo.html页面将执行JavaScript代码:
var stateObj = { foo: "bar" };
history.pushState(stateObj, "page 2", "bar.html");
这时浏览器的地址栏将显示http:// mysite.com/bar.html,但不会加载bar.html页面,也不会检查bar.html是否存在。
如果现在导航到http://mysite.com/页面,然后单击“后退”按钮,此时地址栏会显示http://mysite.com/bar.html,并且会触发popstate事件,该事件中的状态对象会包含stateObj的一个拷贝。
2.pushState()方法
pushState()方法包含3个参数,简单说明如下:
第1个参数:状态对象。状态对象是一个JavaScript对象直接量,与调用pushState()方法创建的新历史记录条目相关联。无论何时用户导航到新创建的状态,popstate事件都会被触发,并且事件对象的state属性都包含历史记录条目的状态对象的拷贝。
第2个参数:标题。可以传入一个简短的标题,标明将要进入的状态。FireFox浏览器目前忽略该参数,考虑到未来可能会对该方法进行修改,传一个空字符串会比较安全。
第3个参数:可选参数,新的历史记录条目的地址。浏览器不会在调用pushState()方法后加载该地址,不指定的话则为文档当前URL。
提示:调用pushState()方法,类似于设置window.location=‘#foo’,它们都会在当前文档内创建和激活新的历史记录条目。但pushState()有自己的优势:
- 新的URL可以是任意的同源URL,与此相反,使用window.location方法时,只有仅修改hash才能保证停留在相同的document中。
- 根据个人需要决定是否修改URL。相反,设置window.location=‘#foo’,只有在当前hash值不是foo时才创建一条新的历史记录。
- 可以在新的历史记录条目中添加抽象数据。如果使用基于hash的方法,只能把相关数据转码成一个很短的字符串。
3.replaceState()方法
history.replaceState()与history.pushState()用法相同,都包含3个相同的参数。不同之处:pushState()是在history栈中添加一个新的条目,replaceState()是替换当前的记录值。例如,history栈中有两个栈块,一个标记为1,另一个标记为2,现在有第三个栈块,标记为3。当执行pushState()时,栈块3将被添加栈中,栈就有3个栈块。而当执行replaceState()时,将使用栈块3替换当前激活的栈块2,history的记录条数不变。也就是说,pushState()会让history的数量加1。
提示:为了响应用户的某些操作,需要更新当前历史记录条目的状态对象或URL时,使用replaceState()方法会特别合适。
4.popstate事件
每当激活的历史记录发生变化时,都会触发popstate事件。如果被激活的历史记录条目是由pushState()创建,或者是被replaceState()方法替换,popstate事件的状态属性将包含历史记录的状态对象的一个拷贝。
注意:当浏览会话历史记录时,不管是单击浏览器工具栏中“前进”或者“后退”按钮,还是使用JavaScript的history.go()和history.back()方法,popstate事件都会被触发。
5.读取历史状态
在页面加载时,可能会包含一个非空的状态对象,这种情况是会发生的。例如,如果页面中使用pushState()或replaceState()方法设置了一个状态对象,然后重启浏览器。当页面重新加载时,页面会触发onload事件,但不会触发popstate事件。但是,如果读取history.state属性,会得到一个与popstate事件触发时一样的状态对象。
可以直接读取当前历史记录条目的状态,而不需要等待popstate事件:
var currentState = history.state;
5、screen对象
screen对象存储客户端屏幕信息,这些信息可以用来探测客户端硬件配置。利用screen对象可以优化程序设计,提升用户体验。例如,根据显示器屏幕大小选择使用图像的大小,或者根据显示器的颜色深度选择使用16色图像或8色图像,或者打开新窗口时设置居中显示等。
【示例】演示让弹出的窗口居中显示:
function center(url){ //窗口居中处理函数
var w = screen.availWidth / 2; //获取客户端屏幕的宽度一半
var h = screen.availHeight/2; //获取客户端屏幕的高度一半
var t = (screen.availHeight - h)/2; //计算居中显示时顶部坐标
var l = (screen.availWidth - w)/2; //计算居中显示时左侧坐标
var p = "top=" + t + ",left=" + l + ",width=" + w + ",height=" +h;
//设计坐标参数字符串
var win = window.open(url,"url",p); //打开指定的窗口,并传递参数
win.focus(); //获取窗口焦点
}
center("https://www.baidu.com/"); //调用该函数
注意:不同浏览器在解析screen对象的width和height属性时存在差异。
6、document对象
document对象代表当前文档,可以使用window对象的document属性进行访问。
6.1、访问文档对象
当浏览器加载文档后,会自动构建文档对象模型,把文档中每个元素都映射到一个数据集合中,然后以document进行访问。document对象与它所包含的各种节点(如表单、图像和链接)构成早期的文档对象模型(DOM 0级)。
【示例1】使用name访问文档元素:
<img name="img" src = "bg.gif" />
<form name="form" method="post" action="http://www.mysite.cn/navi/">
</form>
<script>
console.log(document.img.src); //返回图像的地址
console.log(document.form.action); //返回表单提交的路径
</script>
【示例2】使用文档对象集合可以快速检索:
<img src = "bg.gif" />
<form method="post" action="http://www.mysite.cn/navi/">
</form>
<script>
console.log(document.images[0].src); //返回图像的地址
console.log(document.forms[0].action); //返回表单提交的路径
</script>
【示例3】如果设置了name属性,也可以使用关联数组引用对应的元素对象:
<img name="img" src = "bg.gif" />
<form name="form" method="post" action="http://www.mysite.cn/navi/">
</form>
<script>
console.log(document.images["img"].src); //返回图像的地址
console.log(document.forms["form"].action); //返回表单提交的路径
</script>
6.2、动态生成文档内容
使用document对象的write()和writeln()方法可以动态生成文档内容,主要包括以下两种方式:
- 在浏览器解析时动态输出信息。
- 在调用事件处理函数时使用write()或writeln()方法生成文档内容。
write()方法可以支持多个参数,当为它传递多个参数时,这些参数将被依次写入文档。
【示例1】使用write()方法生成文档内容:
document.write('Hello',',','World');
实际上,上面代码与下面的代码用法是相同的:
document.write('Hello,World');
writeln()方法与write()方法完全相同,只不过在输出参数之后附加一个换行符。由于HTML忽略换行符,所以很少使用该方法,不过在非HTML文档输出时使用会比较方便。
【示例2】演示write()和writeln()方法的混合使用:
function f(){
document.write('<p>调用事件处理函数时动态生成的内容</p>');
}
document.write('<p οnclick="f()">文档解析时动态生成的内容</p>');
在页面初始化后,文档中显示文本为“文档解析时动态生成的内容”,而一旦单击该文本后,则write()方法动态输出文本为“调用事件处理函数时动态生成的内容”,并覆盖原来文档中显示的内容。
注意:只能在当前文档正在解析时,使用write()方法在文档中输出HTML代码,即在<script>
标签中调用write()方法,因为这些脚本的执行是文档解析的一部分。
如果从事件处理函数中调用write()方法,那么write()方法动态输出的结果将会覆盖当前文档,包括它的事件处理函数,而不是将文本添加到其中。所以,使用时一定要小心,不可以在事件处理函数中包含write()或writeln()方法。