BOM 是使用 JavaScript 开发 Web 应用程序的核心。
1 window 对象
BOM 的核心是 window 对象,表示浏览器的实例。window 对象在浏览器中有两重身份,一个是
ECMAScript 中的 Global 对象,另一个就是浏览器窗口的 JavaScript 接口。
1.1 Global 作用域
因为 window 对象被复用为 ECMAScript 的 Global 对象,所以通过 var 声明的所有全局变量和函
数都会变成 window 对象的属性和方法。比如:
var age = 29;
var sayAge = () => alert(this.age);
alert(window.age); // 29
sayAge(); // 29
window.sayAge(); // 29
因为 sayAge()存在于全局作用域,this.age 映射到 window.age,如果在这里使用 let 或 const 替代 var,则不会把变量添加给全局对象。
1.2 窗口关系
top 对象始终指向最上层(最外层)窗口,即浏览器窗口本身。而 parent 对象则始终指向当前窗
口的父窗口。如果当前窗口是最上层窗口,则 parent 等于 top(都等于 window)。最上层的 window
如果不是通过 window.open()打开的,那么其 name 属性就不会包含值。self 对象,它是终极 window 属性,始终会指向 window。
1.3 窗口位置与像素比
screenLeft 和screenTop 属性,用于表示窗口相对于屏幕左侧和顶部的位置 ,返回值的单位是 CSS 像素。可以使用 moveTo()和 moveBy()方法移动窗口。
// 把窗口移动到左上角
window.moveTo(0,0);
// 把窗口向下移动 100 像素
window.moveBy(0, 100);
像素比
手机屏幕的物理分辨率可能是 1920×1080,但因为其像素可能非常小,所以浏览器就需
要将其分辨率降为较低的逻辑分辨率,比如 640×320。这个物理像素与 CSS 像素之间的转换比率由
window.devicePixelRatio 属性提供。对于分辨率从 1920×1080 转换为 640×320 的设备,window.
devicePixelRatio 的值就是 3。这样一来,12 像素(CSS 像素)的文字实际上就会用 36 像素的物理
像素来显示。
window.devicePixelRatio 实际上与每英寸像素数(DPI,dots per inch)是对应的。DPI 表示单
位像素密度,而 window.devicePixelRatio 表示物理像素与逻辑像素之间的缩放系数。
1.4 窗口大小
outerWidth 和 outerHeight 返回浏览器窗口自身的大小(不管是在最外层 window 上使用,还是在窗格中使用)。innerWidth和 innerHeight 返回浏览器窗口中页面视口的大小(不包含浏览器边框和工具栏)。
document.documentElement.clientWidth 和 document.documentElement.clientHeight
返回页面视口的宽度和高度。
可以使用resizeTo()和resizeBy()方法调整窗口大小。这两个方法都接收两个参数,resizeTo()
接收新的宽度和高度值,而 resizeBy()接收宽度和高度各要缩放多少。下面看个例子:
// 缩放到 100×100
window.resizeTo(100, 100);
// 缩放到 200×150
window.resizeBy(100, 50);
// 缩放到 300×300
window.resizeTo(300, 300);
1.5 视口位置
度量文档相对于视口滚动距离的属性有两对,返回相等的值:window.pageXoffset/window. scrollX 和 window.pageYoffset/window.scrollY。
可以使用 scroll()、scrollTo()和 scrollBy()方法滚动页面:
// 相对于当前视口向下滚动 100 像素
window.scrollBy(0, 100);
// 相对于当前视口向右滚动 40 像素
window.scrollBy(40, 0);
// 滚动到页面左上角
window.scrollTo(0, 0);
// 滚动到距离屏幕左边及顶边各 100 像素的位置
window.scrollTo(100, 100);
1.6 导航与打开新窗口
window.open()方法可以用于导航到指定 URL,也可以用于打开新浏览器窗口。
// 与<a href="http://www.wrox.com" target="topFrame"/>相同
window.open("http://www.wrox.com/", "topFrame");
执行这行代码的结果就如同用户点击了一个 href 属性为"http://www.wrox.com",target 属
性为"topFrame"的链接。如果有一个窗口名叫"topFrame",则这个窗口就会打开这个 URL;否则就
会打开一个新窗口并将其命名为"topFrame"。第二个参数也可以是一个特殊的窗口名,比如_self、
_parent、_top 或_blank。
1.弹出窗口
window.open()第三个参数,即特性字符串,用于指定新窗口的配置。如果打开的不是新窗口,则忽略第三个参数。
设置 | 值 | 说明 |
---|---|---|
resizable | “yes"或"no” | 表示是否可以拖动改变新窗口大小。默认为"no" |
toolbar | “yes"或"no” | 表示是否显示工具栏。默认为"no" |
新创建窗口的 window 对象有一个属性 opener,指向打开它的窗口。这个属性只在弹出窗口的最
上层 window 对象(top)有定义,是指向调用 window.open()打开它的窗口或窗格的指针。
虽然新建窗口中有指向打开它的窗口的指针,但反之则不然。窗口不会跟踪记录自己打开的新窗口,
因此开发者需要自己记录。
把 opener 设置为 null 表示新打开的标签页不需要与打开它的标签页通信,因此可以在独立进程
中运行。这个连接一旦切断,就无法恢复了。
2.安全限制
弹出窗口有段时间被在线广告用滥了。为了让用户能够区分清楚,浏览器开始对弹窗施加限制。
3. 弹窗屏蔽程序
所有现代浏览器都内置了屏蔽弹窗的程序,因此大多数意料之外的弹窗都会被屏蔽。那么 window.open()很可能会返回 null。此时,只要检查这个方法的返回值就可以知道弹窗是否被屏蔽了,
在浏览器扩展或其他程序屏蔽弹窗时,window.open()通常会抛出错误。因此要准确检测弹窗是
否被屏蔽,除了检测 window.open()的返回值,还要把它用 try/catch 包装起来,像这样:
let blocked = false;
try {
let wroxWin = window.open("http://www.wrox.com", "_blank");
if (wroxWin == null){
} catch (ex){
blocked = true;
}
if (blocked){
alert("The popup was blocked!");
}
1.7 定时器
JavaScript 在浏览器中是单线程执行的,但允许使用定时器指定在某个时间之后或每隔一段时间就
执行相应的代码。setTimeout()用于指定在一定时间后执行某些代码,而 setInterval()用于指定
每隔一段时间执行某些代码。
JavaScript 是单线程的,所以每次只能执行一段代码。为了调度不同代码的执行,JavaScript 维护了一个任务队列。其中的任务会按照添加到队列的先后顺序执行。调用 setTimeout()时,会返回一个表示该超时排期的数值 ID。这个超时 ID 是被排期执行代码的唯一标识符,可用clearTimeout()取消该任务。
注意 所有超时执行的代码(函数)都会在全局作用域中的一个匿名函数中运行,因此函
数中的 this 值在非严格模式下始终指向 window,而在严格模式下是 undefined。如果
给 setTimeout()提供了一个箭头函数,那么 this 会保留为定义它时所在的词汇作用域。
let timeoutId = setTimeout(() => alert("Hello world!"), 1000);
1.8 系统对话框
alert()、confirm()和 prompt()方法(提示框)、print()、find()
// 显示打印对话框
window.print();
// 显示查找对话框
window.find();
2 location 对象
window.location 和 document.location 指向同一个对象。
2.1 查询字符串
URLSearchParams提供了一组标准 API 方法,通过它们可以检查和修改查询字符串。给
URLSearchParams 构造函数传入一个查询字符串,就可以创建一个实例。这个实例上暴露了 get()、
set()和 delete()等方法,可以对查询字符串执行相应操作。下面来看一个例子:
let qs = "?q=javascript&num=10";
let searchParams = new URLSearchParams(qs);
alert(searchParams.toString()); // " q=javascript&num=10"
searchParams.has("num"); // true
searchParams.get("num"); // 10
searchParams.set("page", "3");
alert(searchParams.toString()); // " q=javascript&num=10&page=3"
searchParams.delete("q");
alert(searchParams.toString()); // " num=10&page=3"
for (let param of searchParams) {
console.log(param);
}
// ["q", "javascript"]
// ["num", "10"]
2.2 操作地址
可以通过修改 location 对象修改浏览器的地址。首先,最常见的是使用 assign()方法并传入一
个 URL,如下所示:
location.assign("http://www.wrox.com");
除了 hash 之外,只要修改 location 的一个属性,就会导致页面重新加载新 URL。
注意 修改 hash 的值会在浏览器历史中增加一条新记录。在早期的 IE 中,点击“后退”
和“前进”按钮不会更新 hash 属性,只有点击包含散列的 URL 才会更新 hash 的值。
replace()这个方法接收一个 URL 参数,但重新加载后不会增加历史记录。调用 replace()之后,用户不能回到前一页。
location.reload(); // 重新加载,可能是从缓存加载
location.reload(true); // 重新加载,从服务器加载
脚本中位于 reload()调用之后的代码可能执行也可能不执行,这取决于网络延迟和系统资源等因
素。为此,最好把 reload()作为最后一行代码。
3 navigator 对象
只要浏览器启用 JavaScript,navigator 对象就一定存在。navigator 对象的属性通常用于确定浏览器的类型
3.1 检测插件
检测浏览器是否安装了某个插件是开发中常见的需求。除 IE10 及更低版本外的浏览器,都可以通
过 plugins 数组来确定。这个数组中的每一项都包含如下属性。
name:插件名称。
description:插件介绍。
filename:插件的文件名。
length:由当前插件处理的 MIME 类型数量。
window.navigator.plugins
旧版本 IE 中的插件检测
IE10 及更低版本中检测插件的问题比较多,因为这些浏览器不支持 Netscape 式的插件。在这些 IE
中检测插件要使用专有的 ActiveXObject
// 在旧版本 IE 中检测插件
function hasIEPlugin(name) {
try {
new ActiveXObject(name);
return true;
} catch (ex) {
return false;
}
}
// 检测 Flash
alert(hasIEPlugin("ShockwaveFlash.ShockwaveFlash"));
3.2 注册处理程序
现代浏览器支持 navigator 上的(在 HTML5 中定义的)registerProtocolHandler()方法。这个方法可以把一个网站注册为处理某种特定类型信息应用程序。registerProtocolHandler()方法,必须传入 3 个参数:要处理的协议(如"mailto"或"ftp")、处理该协议的 URL,以及应用名称。
4 screen 对象
window 的另一个属性 screen 对象,是为数不多的几个在编程中很少用的 JavaScript 对象。保存的是浏览器窗口外面的客户端显示器的信息,比如像素宽度和像素高度。
5 history 对象
history 对象表示当前窗口首次使用以来用户的导航历史记录。因为 history 是 window 的属性,
所以每个 window 都有自己的 history 对象。出于安全考虑,这个对象不会暴露用户访问过的 URL,
但可以通过它在不知道实际 URL 的情况下前进和后退。
5.1 导航
// 后退一页
history.go(-1);
// 前进一页
history.go(1);
// 前进两页
history.go(2);
5.2 历史状态管理
hashchange 会在页面 URL 的散列变化时被触发,开发者可以在此时执行某些操作。而状态管理
API 则可以让开发者改变浏览器 URL 而不会加载新页面。为此,可以使用 history.pushState()方
法。这个方法接收 3 个参数:一个 state 对象、一个新状态的标题和一个(可选的)相对 URL。例如:
let stateObject = {foo:"bar"};
history.pushState(stateObject, "My title", "baz.html");
6 小结
浏览器对象模型(BOM,Browser Object Model)是以 window 对象为基础的,这个对象代表了浏
览器窗口和页面可见的区域。window 对象也被复用为 ECMAScript 的 Global 对象,因此所有全局变
量和函数都是它的属性,而且所有原生类型的构造函数和普通函数也都从一开始就存在于这个对象之
上。本章讨论了 BOM 的以下内容。
要引用其他 window 对象,可以使用几个不同的窗口指针。
通过 location 对象可以以编程方式操纵浏览器的导航系统。通过设置这个对象上的属性,可以改变浏览器 URL 中的某一部分或全部。
使用 replace()方法可以替换浏览器历史记录中当前显示的页面,并导航到新 URL。
navigator 对象提供关于浏览器的信息。提供的信息类型取决于浏览器,不过有些属性如userAgent 是所有浏览器都支持的。
BOM 中的另外两个对象也提供了一些功能。screen 对象中保存着客户端显示器的信息。这些信息
通常用于评估浏览网站的设备信息。history 对象提供了操纵浏览器历史记录的能力,开发者可以确
定历史记录中包含多少个条目,并以编程方式实现在历史记录中导航,而且也可以修改历史记录。