BOM 总结

本文是对 JavaScript 高级程序设计 一书中相关章节的整理,加上自己的理解


JavaScript 不仅可以通过 DOM (Document Object Module/文档对象模型)操作文档,还可以通过 BOM (Browser Object Module/浏览器对象模型)操作浏览器,它是一个用于访问浏览器和屏幕窗口的对象集合。
BOM 提供了很多对象,用于访问浏览器的功能,实现了脚本与浏览器的互操作性。
W3C 已将将 BOM 的主要方面纳入了 HTML5 的规范中。
下面我们来了解一下 BOM 中的几个对象。

1. window 对象

BOM 的核心对象是 window 对象,它表示浏览器的一个实例。
事实上,在浏览器中,window 对象有双重角色,它表示浏览器的一个实例,提供 JavaScript 访问浏览器窗口的接口;又是 JavaScript 脚本中的 Global 对象,在脚本中定义的任何一个对象、变量、函数,都以 window 作为其全局对象,因此有权访问 window 中定义的属性和方法。

1.1 全局作用域

由于 window 对象同时扮演着 JavaScript 中全局对象的角色,因此所有在全局作用域中声明的变量、函数都会变成 window 对象的属性和方法。看下面的例子:

var name = "xiaoming";
function saySomething() {
alert("hi, I'm" + " " + name);
}
alert(window.name); // xiaoming
window.saySomething(); // hi, I'm xianming

虽然全局变量会成为 window 对象的属性,但是全局变量和在 window 对象上直接定义属性还是有一点差别的:全局变量不能通过 delete 操作符删除,而直接在 window 对象上定义的属性可以
除了显示地定义 window 的属性外,还有有两种情况,变量会被隐性地定义为 window 的属性:

  1. 在全局作用域中,不加 var 关键字声明的变量,实际上就是在 window 对象上定义的属性,是可以被删除的。看下面的例子:

    var name = "xiaoming";
    age = 26;
    Object.getOwnPropertyDescriptor(window, "name");
    // configurable: false; enumerabl: true; value: 26; writable: true
    Object.getOwnPropertyDescriptor(window, "age");
    // configurable: true; enumerabl: true; value: 26; writable: true
    delete window.name;
    delete window.age;
    alert(name); // xiaoming
    alert(age); // age is not defined

    从上面的例子中我们可以看到,name 属性的 configurable 描述符的值为 false,表示不可删除;而 age 属性的 configurable 描述符值为 true,表示可删除。

  2. 还有一种情况是,在函数中不加 var 声明的变量,其实也被作为 window 对象的属性进行了声明。我们来看一下例子:

    function saySomething() {
    age = 26;
    alert(age);
    }
    Object.getOwnPropertyDescriptor(window, "age"); // undefined
    saySomething(); // 26
    Object.getOwnPropertyDescriptor(window, "age");
    // value: 45, writable: true, enumerable: true, configurable: true
    delete window.age; // true
    saySomething(); // 26

    从上面代码中我们可以看出来,只有在函数被执行过一次后,age 才会被声明,然后查看它的描述符,发现 configurable 值为 true,age 可以被删除。
    可是为什么删除了以后还能被访问呢,那是因为函数被调用的同时又进行了一次 age 的声明,age 又变成了 window 的一个属性。

1.2 窗口关系

1.2.1 窗口位置

用来确定和修改窗口位置的属性有两套标准:screenLeft/screenTop 和 screenX/screenY,每对属性都分别表示了窗口相对于屏幕左边和上边的位置。下面的表格是各大浏览器对这两套标准的支持情况:

IE Safari Chrome Opera Firefox
screenLeft/Top yes yes yes yes no
screenX/Y no yes yes yes(not same) yes

下面是一个跨浏览器的解决方案:

var leftPos = (typeof window.screenLeft === "number") ?
window.screenLeft : window.screenX;
var topPos = (typeof window.screenTop === "number") ?
window.screenTop : window.screenY;

这个例子运用二元操作符首先确定 screenLeft 和 screenTop 属性是否存在,如果是(在 IE、
Safari、 Opera 和 Chrome 中),则取得这两个属性的值。如果不存在(在 Firefox 中),则取得 screenX 和 screenY 的值。

但是,需要注意的是,在 IE 和 Opera 中,screenLeft 和 screenTop 保存的是频幕左边和上边到 window 对象表示的页面可见区域的距离,即距离里包括了浏览器工具栏的的高度。但是,在 Chrome、 Firefox 和 Safari 中, screenTop 或 screenY 中保存的是整个浏览器窗口相对于屏幕的坐标值。

无法在跨浏览器的情况下取得窗口左边和上边的精确坐标值。

1.2.2 窗口大小

跨浏览器确定一个窗口的大小不是一件简单的事。虽然最终无法确定浏览器窗口本身的大小,但是却可以确定页面视口的大小,如下所示:

var pageWidth = window.innerWidth,
pageHeight = window.innnerHeight;
if (typeof pageWidth !== "number") {
if (document.compatMode === "CSS1Compat") {
pageWidth = document.documentElement.clientWidth;
pageHeight = document.documentElement.clientHeight;
} else {
pageWidth = document.body.clientWidth;
pageHeight = document.body.clientHeight;
}
}

1.2.3 导航和打开窗口

使用 window.open() 可以导航到一个特定的 URL,也可以打开一个新的浏览器窗口。
这个方法接受 4 个参数,分别是:要加载的 URL、窗口目标、一个特性字符串、一个表示新页面是否取代浏览器历史记录中当前加载页面的布尔值。
通常只需要传递第一个参数,打开一个新窗口。

如果传递了第二个参数,而且该参数是已有窗口或框架的名称,那么就会在具有该名称的窗口或框架中加载第一个参数指定的 URL。

// 等同于 <a href = "https://www.google.com" target = "myFrame">
window.open("https://www.google.com", "myFrame");

调用这行代码,就如同用户单击了 href 属性为 https://www.google.com,target 属性为 “myFrame” 的一个链接。如果有一个名为 “myFrame” 的窗口或者框架,就会在该窗口或框架加载这个 URL;否则就会创建一个新的窗口并将其命名为 ”myFrame”。
此外,第二个参数也可以是下列任何一个特殊的窗口名:_self、 _parent、 _top、 _blank

  • _slef: 在同一框架或窗口中打开所链接的文档。
  • _blank: 在新窗口打开网页
  • _parent: 将链接的文件载入含有该链接框架的父框架集或父窗口中。如果含有该链接的框架不是嵌套的,则在浏览器全屏窗口中载入链接的文件,就象_self参数一样。
  • _top: 在当前的整个浏览器窗口中打开所链接的文档,因而会删除所有框架

框架,就是一个浏览器页面分为几个部分,每个部分都是一个框架,各个框架互不干扰。

弹出窗口

如果第二个参数并不是一个已经存在的窗口或框架,那么该方法就会根据第三个参数中的设置创建一个新窗口或新标签页。
如果没有传入第三个参数,那么就会打开一个带有全部默认设置(工具栏、地址栏、状态栏等)的新浏览器窗口。在不带新窗口的情况下忽略第三个参数。

window.open("https://www.google.com", "myWindow", "width = 200, height = 200, left = 10, top = 10, resizable = yes");

上面的代码会打开一个新的窗口,这个窗口初始大小为 200 x 200 像素,并且距离屏幕上沿和左边各10像素,而且窗口大小是可以调节的。

window.open() 方法会返回一个指向新窗口的引用。我们可以对其调整大小,移动位置,或关闭它:

var myWindow = window.open("https://www.google.com", "myWindow", "width = 200, height = 200, left = 10, top = 10, resizable = yes");
myWindow.resizeTo(500, 500);
myWindow.moveTo(100, 100);
myWindow.close();

检测弹出窗口是否被屏蔽

大多数浏览器都有内置的弹出窗口屏蔽程序,没有的也可以安装插件进行屏蔽,下面的方法可以检测弹窗是否被屏蔽了:

var blocked = false;
try {
var myWindow = window.open("https://www.google.com", "_blank");
// 浏览器内置屏蔽会返回 null
if (myWindow === null) {
blocked = true;
}
// 插件屏蔽会抛出错误
} catch(e) {
blocked = true;
}
if (blocked) {
alert("The popup was blocked!");
}

1.3 系统对话框

  • alert()
    警告对话框,有一个 “OK” 按钮
  • confirm()
    确认对话框,有一个 “OK” 和一个 “Cancel” 按钮,根据用户点击的按钮返回相应的布尔值

    if (confirm("Are you sure?")) {
    alert("I'm so glad you're sure! ");
    } else {
    alert("I'm sorry to hear you're not sure. ");
    }
  • prompt()
    接收两个参数,一个是提示信息字符串,另一个是输入框中的提示内容

    prompt("What's your name?","Michael")

2. location 对象

location 是最有用的 BOM 对象之一,它提供了与当前窗口中加载页面的 URL 有关的信息,还提供了一些导航信息。
事实上,location 对象是一个很特别的对象,它既是 window 对象的属性,也是 document 对象的属性;换句话说,window.location 和 document.location 引用的是同一个对象。
location 将 URL 解析为独立的片段,保存为其属性值,下表列出了 location 对象常用的属性:

属性名 例子 说明
href https://www.google.com 返回当前加载页面的完整 URL,location.toString() 的值也是这个
protocol “https:” 返回页面使用的协议
host “www.google.com:80” 返回服务器名称和端口号
hostname “www.google.com” 返回不带端口号的服务器名称
port “8080” 返回端口号,如果没有端口号则返回空字符串
pathname “/sda/“ 返回 URL 中的目录和(或)文件名
search “?q=javascript” 返回 URL 的查询字符串

url

2.1 查询字符串参数

尽管 location.search 返回 URL 从问号到末尾的所有内容,但却没有办法逐个访问其中的每个查询字符串参数,下面的代码用于解析查询字符串,然后返回一个包含所有参数的对象:

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;
// 逐个将每一项添加到 args 对象中
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;
}

2.2 位置操作

  1. 修改当前页面,并生成历史记录(可后退)

    • location.assign("https://www.google.com");
      这是最常用的方法,使用 assign() 方法并为其传递一个 URL。
      这样就可以立即打开新的 URL 并在浏览器的历史记录中生成一条记录。
      如果将 location.hrefwindow.location 设置为一个 URL 值,也会以该值调用 assign() 方法。
    • 另外,修改 location 对象的其他属性也可以改变当前加载的页面:
      location.search = "?q=javascript";
      location.port = 8080;
  2. 修改当前页面,不生成历史记录(不可后退)
    location.replace("https://www.google.com");
    在调用 replace() 方法之后,用户不能回到之前的页面。

  3. 重新加载当前页面,reload()
    location.reload(); //重新加载(有可能从缓存中加载) location.reload(true); //重新加载(从服务器重新加载)

3. navigator 对象

navigator 对象包含了浏览器的信息。
有两个比较有用的属性:

  1. platform
    表示当前页面加载在什么操作系统上
    platform: "MacIntel"
    platform: "Win32"
  2. userAgent
    浏览器的用户代理字符串,包含了很多客户端的信息:
    userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36"
    其中,AppleWebKit/537.36 (KHTML, like Gecko) 是浏览器的内核和内核版本号
    Chrome/54.0.2840.98 Safari/537.36 是浏览器的类型和版本号

4. history 对象

主要的属性有 length,代表历史记录中条目的数量;
主要的方法有:

  • history.forward(num)
    num 为正整数,代表页面前进的数目
  • history.back(num)
    num 为正整数,代表页面后退的数目
  • history.go(num)
    num 为正负整数,正整数代表页面前进的数目,负整数代表页面后退的数目

5. screen 对象

这个对象似乎没有什么实际用途

6. 事件

  1. load
    文档和所有图片加载完毕时
  2. unload
    离开当前文档时
  3. beforeunload
    和 unload 类似,但它提供询问用户是否确定离开的机会
  4. resize
    拖动改变浏览器窗口大小时
  5. scroll
    拖动滚动浏览器时