云隙随笔

学习笔记 | JS 高级程序设计-第12章-BOM

发布于 # 学习笔记

BOM

浏览器对象模型(BOM, Browser Object Model)

window 对象

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

在全局中使用 var 定义可以将变量定义于 window 中,而 let 不会添加。

另外,访问未声明的变量会抛出错误,但是可以在window对象上查询是否存在可能未声明的变量。

// 这会导致抛出错误,因为oldValue没有声明
var newValue = oldValue;
// 这不会抛出错误,因为这里是属性查询
// newValue会被设置为undefined
var newValue = window.oldValue;

窗口关系

  • top对象始终指向最上层(最外层)窗口,即浏览器窗口本身。
  • parent对象则始终指向当前窗口的父窗口。如果当前窗口是最上层窗口,则parent等于top(都等于window)。
  • 最上层的window如果不是通过window.open()打开的,那么其name属性就不会包含值。
  • self对象始终会指向window。实际上,self和window就是同一个对象。之所以还要暴露self,就是为了和top、parent保持一致。

窗口位置与像素比

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

移动窗口使用moveTo()和moveBy()。

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

CSS像素(CSS Pixels)

CSS像素是Web开发中使用的统一像素单位。这个单位的背后其实是一个角度:0.0213°。,称为设备独立像素(Device Independent Pixels, DIPs)。它是一个抽象的单位,旨在为开发者提供一个统一的度量标准,使得不同设备和分辨率上的网页元素能够保持大致相同的视觉大小。

物理像素(Physical Pixels)

物理像素是显示屏幕上实际的光点,它们是构成屏幕显示内容的最小单位。不同设备的屏幕可能有不同的物理像素密度,即PPI(Pixels Per Inch,每英寸像素数)。

设备像素比(Device Pixel Ratio, DPR)

由于物理像素的密度可以非常高(尤其是在现代设备上),CSS像素和物理像素之间的关系由设备像素比(DPR)定义。DPR是物理像素数和CSS像素数的比例。例如,如果DPR为2,则意味着一个CSS像素占据了2x2个物理像素。

window.devicePixelRatio实际上与每英寸像素数(DPI, dots per inch)是对应的。DPI表示单位像素密度,而window.devicePixelRatio表示物理像素与逻辑像素之间的缩放系数。

窗口大小

不同的窗口大小属性:

  • outerWidth和outerHeight返回浏览器窗口自身的大小(不管是在最外层window上使用,还是在窗格<frame>中使用)。
  • innerWidth和innerHeight返回浏览器窗口中页面视口的大小(不包含浏览器边框和工具栏)。
  • document.documentElement.clientWidth和document.documentElement.clientHeight返回页面视口的宽度和高度。

可以使用resizeTo()和resizeBy()方法调整窗口大小。这两个方法都接收两个参数,resizeTo()接收新的宽度和高度值,而resizeBy()接收宽度和高度各要缩放多少。

视口位置

  • window.pageXoffset/window. scrollX
  • window.pageYoffset/window.scrollY

滚动页面使用 scroll()、scrollTo() 和 scrollBy()。

这几个方法也都接收一个ScrollToOptions字典,除了提供偏移值,还可以通过behavior属性告诉浏览器是否平滑滚动。

    // 正常滚动
    window.scrollTo({
      left: 100,
      top: 100,
      behavior: 'auto'
    });
    // 平滑滚动
    window.scrollTo({
      left: 100,
      top: 100,
      behavior: 'smooth'
    });

导航与打开新窗口

window.open()方法可以用于导航到指定URL,也可以用于打开新浏览器窗口。这个方法接收4个参数:

  1. 要加载的URL
  2. 目标窗口(如果不是已有窗口,则会打开一个新窗口或标签页)
  3. 特性字符串
  4. 表示新窗口在浏览器历史记录中是否替代当前加载页面的布尔值。

例子:

    let wroxWin = window.open("http://www.wrox.com/",
                  "wroxWindow",
                  "height=400, width=400, top=10, left=10, resizable=yes");
    // 缩放
    wroxWin.resizeTo(500, 500);
    // 移动
    wroxWin.moveTo(100, 100);

新创建窗口的window对象有一个属性opener,指向打开它的窗口。这个属性只在弹出窗口的最上层window对象(top)有定义,是指向调用window.open()打开它的窗口或窗格的指针。

在某些浏览器中,每个标签页会运行在独立的进程中。如果一个标签页打开了另一个,而window对象需要跟另一个标签页通信,那么标签便不能运行在独立的进程中。在这些浏览器中,可以将新打开的标签页的opener属性设置为null,表示新打开的标签页可以运行在独立的进程中。

把opener设置为null表示新打开的标签页不需要与打开它的标签页通信,因此可以在独立进程中运行。这个连接一旦切断,就无法恢复了。

系统对话框

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

confirm() 返回确认和取消按钮的对话框。 要知道用户单击了OK按钮还是Cancel按钮,可以判断confirm()方法的返回值:true表示单击了OK按钮,false表示单击了Cancel按钮或者通过单击某一角上的X图标关闭了确认框。

最后一种对话框是提示框,通过调用prompt()方法来显示。提示框的用途是提示用户输入消息。除了OK和Cancel按钮,提示框还会显示一个文本框,让用户输入内容。prompt()方法接收两个参数:要显示给用户的文本,以及文本框的默认值(可以是空字符串)。 如果用户单击了OK按钮,则prompt()会返回文本框中的值。如果用户单击了Cancel按钮,或者对话框被关闭,则prompt()会返回null。

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

location对象

location是最有用的BOM对象之一,提供了当前窗口中加载文档的信息,以及通常的导航功能。这个对象独特的地方在于,它既是window的属性,也是document的属性。也就是说,window.location和document.location指向同一个对象。

location对象不仅保存着当前加载文档的信息,也保存着把URL解析为离散片段后能够通过属性访问的信息。

查询字符串

虽然location.search返回了从问号开始直到URL末尾的所有内容,但没有办法逐个访问每个查询参数。

而 URLSearchParams 提供了一组标准API方法,通过它们可以检查和修改查询字符串。给URLSearchParams构造函数传入一个查询字符串,就可以创建一个实例。

大多数支持URLSearchParams的浏览器也支持将URLSearchParams的实例用作可迭代对象。

操作地址

location.assign("http://www.xxx.com")这行代码会立即启动导航到新URL的操作,同时在浏览器历史记录中增加一条记录。 如果给location.href或window.location设置一个URL,也会以同一个URL值调用assign()方法。

修改location对象的属性也会修改当前加载的页面。其中,hash、search、hostname、pathname和port属性被设置为新值之后都会修改当前URL。 除了hash之外,只要修改location的一个属性,就会导致页面重新加载新URL。

在以前面提到的方式修改URL之后,浏览器历史记录中就会增加相应的记录如果不希望增加历史记录,可以使用replace()方法。这个方法接收一个URL参数,但重新加载后不会增加历史记录。

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

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

window.navigator 是一个包含了用户浏览器信息的对象,它是 window 对象的一个属性,代表了浏览器的状态和身份。navigator 对象提供了很多属性和方法,可以让开发者获取到关于浏览器的信息,并且与之交互。

  1. 浏览器检测:可以通过 navigator 对象来检测用户正在使用的浏览器及其版本。常用的属性包括 navigator.userAgent、navigator.appVersion、navigator.appName 等。
  2. 平台信息:可以使用 navigator.platform 来获取用户操作系统的信息。
  3. 网络状态:navigator 对象提供了 navigator.onLine 属性,可以用来检测用户的设备是否连接到网络。
  4. 地理位置:通过 navigator.geolocation 对象,可以获取用户的地理位置信息(当然,这需要用户的授权)。
  5. 多媒体功能:navigator 对象提供了访问用户媒体设备(如摄像头和麦克风)的能力,通过 navigator.mediaDevices。
  6. 插件信息:过去,navigator 对象可以用来检测浏览器安装的插件,但由于安全和隐私的原因,这个功能已经被逐渐淘汰。
  7. 注册协议处理器:如之前所述,navigator.registerProtocolHandler 允许网站注册为特定协议(例如 mailto 或 webcal)的处理程序。
  8. 服务工作线程:navigator.serviceWorker 允许注册和管理 Service Workers,这些是运行在浏览器背后的脚本,可以用来支持离线体验、网络请求拦截和背景同步等。
  9. 推送通知:navigator 对象可以用来管理和控制Web推送通知。
  10. 语言和国际化:navigator.language 属性表示用户的首选语言,navigator.languages 则是一个包含用户首选语言的数组。
  11. 性能分析:navigator 对象提供了 navigator.sendBeacon 方法,可以在卸载文档之前异步地发送小量数据到服务器,用于性能分析和统计。
  12. 电池状态:navigator.getBattery 方法(如果可用)允许访问设备电池状态的信息。
  13. 剪贴板操作:navigator.clipboard 提供了读取和修改剪贴板内容的能力。
  14. 凭证管理:navigator.credentials 用于访问和管理用户的凭证信息。
  15. 硬件并发:navigator.hardwareConcurrency 属性表示可用于运行线程的逻辑处理器的数量。

检测插件

都可以通过window.navigator.plugins数组来确定。这个数组中的每一项都包含如下属性。

  • name:插件名称。
  • description:插件介绍。
  • filename:插件的文件名。
  • length:由当前插件处理的MIME类型数量。

通常,name属性包含识别插件所需的必要信息,尽管不是特别准确。检测插件就是遍历浏览器中可用的插件,并逐个比较插件的名称

注册处理程序

现代浏览器支持navigator上的(在HTML5中定义的)registerProtocolHandler()方法。

要使用registerProtocolHandler()方法,必须传入3个参数:要处理的协议(如”mailto”或”ftp”)、处理该协议的URL,以及应用名称。

navigator.registerProtocolHandler("mailto", "http://www.somemailclient.com?cmd=%s", "Some Mail Client");

当用户点击一个 mailto 链接时,浏览器会打开 http://www.somemailclient.com 这个地址,并且 %s 会被替换为实际的 mailto URL。例如,如果 mailto 链接是 mailto:[email protected],那么 %s 就会被替换为 mailto:[email protected]。

screen对象

这个对象中保存的纯粹是客户端能力信息,也就是浏览器窗口外面的客户端显示器的信息,比如像素宽度和像素高度。每个浏览器都会在screen对象上暴露不同的属性。

history对象

history对象表示当前窗口首次使用以来用户的导航历史记录。因为history是window的属性,所以每个window都有自己的history对象。出于安全考虑,这个对象不会暴露用户访问过的URL,但可以通过它在不知道实际URL的情况下前进和后退。

导航

go()方法可以在用户历史记录中沿任何方向导航,可以前进也可以后退。这个方法只接收一个参数,这个参数可以是一个整数,表示前进或后退多少步。 go()有两个简写方法:back()和forward()。 history对象还有一个length属性,表示历史记录中有多个条目。

注意 如果页面URL发生变化,则会在历史记录中生成一个新条目。对于主流浏览器,这包括改变URL的散列值(因此,把location.hash设置为一个新值会在这些浏览器的历史记录中增加一条记录)。这个行为常被单页应用程序框架用来模拟前进和后退,这样做是为了不会因导航而触发页面刷新。

历史状态管理

hashchange会在页面URL的散列变化时被触发,开发者可以在此时执行某些操作。而状态管理API则可以让开发者改变浏览器URL而不会加载新页面。为此,可以使用history.pushState()方法。这个方法接收3个参数:一个state对象、一个新状态的标题和一个(可选的)相对URL。

    let stateObject = {foo:"bar"};
    history.pushState(stateObject, "My title", "baz.html");

pushState()方法执行后,状态信息就会被推到历史记录中,浏览器地址栏也会改变以反映新的相对URL。除了这些变化之外,即使location.href返回的是地址栏中的内容,浏览器页不会向服务器发送请求。第二个参数并未被当前实现所使用,因此既可以传一个空字符串也可以传一个短标题。第一个参数应该包含正确初始化页面状态所必需的信息。为防止滥用,这个状态的对象大小是有限制的,通常在500KB~1MB以内。

因为pushState()会创建新的历史记录,所以也会相应地启用“后退”按钮。此时单击“后退”按钮,就会触发window对象上的popstate事件。popstate事件的事件对象有一个state属性,其中包含通过pushState()第一个参数传入的state对象:

    window.addEventListener("popstate", (event) => {
      let state = event.state;
      if (state) { // 第一个页面加载时状态是null
        processState(state);
      }
    });

replaceState 传入的参数和 pushState 一致,但是只会覆盖当前历史记录。

注意,使用HTML5状态管理时,要确保通过pushState()创建的每个“假”URL背后都对应着服务器上一个真实的物理URL。否则,单击“刷新”按钮会导致404错误。所有 SPA 框架都必须通过服务器或客户端的某些配置解决这个问题。