# BOM

浏览器对象模型(BOM,Browser Object Model),是使用 JavaScript 开发 Web 应用程序的核心。是实现 Web 开发与浏览器之间互相操作的基础。

BOM 主要包含五个基础对象:

  • window:表示浏览器实例
  • location:加载文档的信息和常用导航功能实例
  • navigator:客户端标识和信息的对象实例
  • screen:客户端显示器信息
  • history:当前窗口建立以来的导航历史记录

# 1、window

window 对象,表示浏览器的实例。window 对象在浏览器中有两重身份,一个是 ECMAScript 中的 Global 对象,另一个就是浏览器窗口的 JavaScript 接口。这意味着网页中定义的所有对象、变量和函数都以 window 作为其 Global 对象,都可以访问其上定义的 parseInt() 等全局方法。

# 1.1 Global 作用域

在全局作用于下,所有使用 var 声明的变量和函数都会成为 window 对象的属性和方法。并且浏览器 API 和多数构造函数 都会以 window 对象的属性。不同浏览器 window 对象的属性可能不同。

# 1.2 窗口关系

window 对象的 top 属性始终指向最外层的窗口,及浏览器窗口本身。

window 对象的 parent 属性始终指向当前窗口的父窗口,如果当前窗口就是最外层窗口,则 top 等于 parent。

window 对象的 self 属性始终指向自身。

# 1.3 窗口位置与像素比

screenLeft 和 screenTop 属性,用于表示窗口相对于屏幕左侧和顶部的位置,返回值的单位是 CSS 像素。

可以使用 moveTo() 和 moveBy() 方法移动窗口。

// 把窗口移动到左上角
window.moveTo(0, 0);
// 把窗口向下移动 100 像素
window.moveBy(0, 100);

// 把窗口移动到坐标位置(200, 300)
window.moveTo(200, 300);
// 把窗口向左移动 50 像素
window.moveBy(-50, 0);
1
2
3
4
5
6
7
8
9

依浏览器而定,以上方法可能会被部分或全部禁用。

window.devicePixelRatio 表示物理像素与逻辑像素之间的缩放系数。

获取浏览器缩放比:

function detectZoom() {
  var ratio = 0;

  if (window.devicePixelRatio !== undefined) {
    ratio = window.devicePixelRatio;
  } else if (
    window.outerWidth !== undefined &&
    window.innerWidth !== undefined
  ) {
    ratio = window.outerWidth / window.innerWidth;
  }

  if (ratio) {
    ratio = Math.round(ratio * 100);
  }

  return ratio;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

设置根据浏览器缩放比来缩放页面大小:

const body = document.getElementsByTagName('body')[0];
body.style['zoom'] = 1 / window.devicePixelRatio;
1
2

# 1.4 窗口大小

  • innerWidth 返回以像素为单位的窗口的内部宽度。如果垂直滚动条存在,则这个属性将包括它的宽度。(不包含浏览器边框和工具栏)
  • innerHeight 浏览器窗口的视口(viewport)高度(以像素为单位);如果有水平滚动条,也包括滚动条高度。(不包含浏览器边框和工具栏)
  • outerWidth 获取浏览器窗口外部的宽度。表示整个浏览器窗口的宽度,包括侧边栏(如果存在)、窗口镶边(window chrome)和调正窗口大小的边框(window resizing borders/handles)。
  • outerHeight 获取整个浏览器窗口的高度(单位:像素),包括侧边栏(如果存在)、窗口镶边(window chrome)和窗口调正边框(window resizing borders/handles)。
const intViewportWidth = window.innerWidth;
const intViewportHeight = window.innerHeight;

const outWindowWidth = window.outerWidth;
const outWindowHeight = window.outerHeight;
1
2
3
4
5

使用 resizeTo() 和 resizeBy() 方法调整窗口大小。

resizeTo() 接收新的宽度和高度值,而 resizeBy() 接收宽度和高度各要缩放多少。

// 缩放到 100×100
window.resizeTo(100, 100);
// 缩放到 200×150
window.resizeBy(100, 50);
// 缩放到 300×300
window.resizeTo(300, 300);
1
2
3
4
5
6

# 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);

// 通过 behavior 属性告诉浏览器是否平滑滚动
// 正常滚动
window.scrollTo({
  left: 100,
  top: 100,
  behavior: 'auto',
});
// 平滑滚动
window.scrollTo({
  left: 100,
  top: 100,
  behavior: 'smooth',
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 1.6 导航与打开新窗口

window.open() 方法可以用于导航到指定 URL,也可以用于打开新浏览器窗口。

这个方法接收 4 个参数:要加载的 URL、目标窗口、特性字符串和表示新窗口在浏览器历史记录中是否替代当前加载页面的布尔值。

// 与<a href="http://www.xxx.com" target="topFrame"/>相同
window.open('http://www.xxx.com/', 'topFrame');
1
2

# 1.7 定时器

  • setTimeout() 超时任务:等待一段时间之后再执行内部的代码,会返回一个超时 ID。
  • setInterval() 定时任务:每隔一段时间执行一次内部的代码,会返回一个定时 ID。
  • clearTimeout() 清除指定/所有超时任务。
  • clearInterval() 清除指定/所有定时任务。

所有超时执行的代码(函数)都会在全局作用域中的一个匿名函数中运行,因此函数中的 this 值在非严格模式下始终指向 window,而在严格模式下是 undefined。如果给 setTimeout()提供了一个箭头函数,那么 this 会保留为定义它时所在的词汇作用域。

# 1.8 系统对话框

使用 alert()、confirm() 和 prompt() 方法,可以让浏览器调用系统对话框向用户显示消息。这些对话框与浏览器中显示的网页无关,而且也不包含 HTML。它们的外观由操作系统或者浏览器决定,无法使用 CSS 设置。此外,这些对话框都是同步的模态对话框,即在它们显示的时候,代码会停止执行,在它们消失以后,代码才会恢复执行。

用法:

if (confirm('Are you sure?')) {
  alert("I'm so glad you're sure!");
} else {
  alert("I'm sorry to hear you're not sure.");
}

let result = prompt('What is your name? ', '');
if (result !== null) {
  alert('Welcome, ' + result);
}
1
2
3
4
5
6
7
8
9
10

JavaScript 还可以显示另外两种对话框:find() 和 print()。这两种对话框都是异步显示的,即控制权会立即返回给脚本。用户在浏览器菜单上选择“查找”(find)和“打印”(print)时显示的就是这两种对话框。通过在 window 对象上调用 find() 和 print() 可以显示它们

// 显示打印对话框
window.print();
// 显示查找对话框
window.find();
1
2
3
4

# 2、location 对象

location 是最有用的 BOM 对象之一,提供了当前窗口中加载文档的信息,以及通常的导航功能。

既是 window 的属性,也是 document 的属性。window.location 和 document.location 指向同一个对象。

假设浏览器当前加载的 URL 是 http://foouser:barpassword@www.wrox.com:80/WileyCDA/?q=javascript#contents

location 对象的内容如下表所示:

属 性 说 明
location.hash "#contents" URL 散列值(井号后跟零或多个字符),如果没有则为空字符串
location.host "www.wrox.com:80" 服务器名及端口号
location.hostname "www.wrox.com" 服务器名
location.href "http://www.wrox.com:80/WileyCDA/?q=javascript#contents" 当前加载页面的完整 URL。location 的 toString() 方法返回这个值
location.pathname "/WileyCDA/" URL 中的路径和(或)文件名
location.port "80" 请求的端口。如果 URL 中没有端口,则返回空字符串
location.protocol "http:" 页面使用的协议。通常是"http:"或"https:"
location.search "?q=javascript" URL 的查询字符串。这个字符串以问号开头
location.username "foouser" 域名前指定的用户名
location.password "barpassword" 域名前指定的密码
location.origin "http://www.wrox.com" URL 的源地址。只读

# 2.1 查询字符串

location 的多数信息都可以通过上面的属性获取。但是 URL 中的查询字符串并不容易使用。虽然 location.search 返回了从问号开始直到 URL 末尾的所有内容,但没有办法逐个访问每个查询参数。

let getQueryStringArgs = function () {
  // 取得没有开头问号的查询字符串
  let qs = location.search.length > 0 ? location.search.substring(1) : '',
    // 保存数据的对象
    args = {};
  // 把每个参数添加到 args 对象
  for (let item of qs.split('&').map(kv => kv.split('='))) {
    let name = decodeURIComponent(item[0]), // 解码
      value = decodeURIComponent(item[1]);
    if (name.length) {
      args[name] = value;
    }
  }
  return args;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

URLSearchParams 提供了一组标准 API 方法,通过它们可以检查和修改查询字符串。

这个实例上暴露了 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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 2.2 操作地址

可以通过修改 location 对象修改浏览器的地址。

assign() 方法并传入一个 URL,立即导航到新 URL 的操作,同时在浏览器历史记录中增加一条记录。

如果给 location.href 或 window.location 设置一个 URL,也会以同一个 URL 值调用 assign() 方法。

location.assign('http://www.wrox.com');
window.location = 'http://www.wrox.com';
location.href = 'http://www.wrox.com';
1
2
3

修改 location 对象的属性也会修改当前加载的页面。其中,hash、search、hostname、pathname 和 port 属性被设置为新值之后都会修改当前 URL。

// 假设当前 URL 为 http://www.wrox.com/WileyCDA/
// 把 URL 修改为 http://www.wrox.com/WileyCDA/#section1
location.hash = '#section1';
// 把 URL 修改为 http://www.wrox.com/WileyCDA/?q=javascript
location.search = '?q=javascript';
1
2
3
4
5

使用 replace() 方法。这个方法接收一个 URL 参数,但重新加载后不会增加历史记录。调用 replace() 之后,用户不能回到前一页。

使用 reload() 方法,重新加载当前显示的页面。调用 reload() 而不传参数,页面会以最有效的方式重新加载。也就是说,如果页面自上次请求以来没有修改过,浏览器可能会从缓存中加载页面。如果想强制从服务器重新加载,可以像下面这样给 reload() 传个 true

location.reload(); // 重新加载,可能是从缓存加载
location.reload(true); // 重新加载,从服务器加载
1
2

脚本中位于 reload() 调用之后的代码可能执行也可能不执行,这取决于网络延迟和系统资源等因素。为此,最好把 reload() 作为最后一行代码。

# 3、navigator 对象

客服端标识浏览器的标准,主要用来记录和检测浏览器与设备的主要信息,也可以让脚本注册和查询自己的一些活动(插件)。

navigator 对象的属性通常用于确定浏览器的类型。

# 4、screen 对象

screen 对象中保存的纯粹是客户端能力信息,也就是浏览器窗口外面的客户端显示器的信息

下表总结了这些属性:

属 性 说 明
availHeight 屏幕像素高度减去系统组件高度(只读)
availLeft 没有被系统组件占用的屏幕的最左侧像素(只读)
availTop 没有被系统组件占用的屏幕的最顶端像素(只读)
availWidth 屏幕像素宽度减去系统组件宽度(只读)
colorDepth 表示屏幕颜色的位数;多数系统是 32(只读)
height 屏幕像素高度
left 当前屏幕左边的像素距离
pixelDepth 屏幕的位深(只读)
top 当前屏幕顶端的像素距离
width 屏幕像素宽度
orientation 返回 Screen Orientation API 中屏幕的朝向

# 5 history 对象

浏览器导航历史记录及相关操作的对象。

# 5.1 导航

go() 方法可以在用户历史记录中沿任何方向导航,可以前进也可以后退。

// 后退一页
history.go(-1);
// 前进一页
history.go(1);
// 前进两页
history.go(2);

// 有两个简写方法:back() 和 forward()
// 后退一页
history.back();
// 前进一页
history.forward();
1
2
3
4
5
6
7
8
9
10
11
12

# 5.2 历史状态管理

  • hashchange 事件会在页面 URL 的散列变化时被触发
  • history.pushState() 方法接收 3 个参数:一个 state 对象、一个新状态的标题和一个(可选的)相对 URL。
  • popstate 事件(在 window 对象上):后退时触发
  • history.state 属性:当前的历史记录状态
  • history.replaceState() 方法:接收与 pushState() 一样的前两个参数来更新状态
上次更新: 6/2/2023, 6:55:53 PM