【重走JavaScript之高级程序设计】BOM

BOM(Browser Object Model)

BOM 全称为浏览器对象模型,BOM是利用JavaScript开发Web应用程序的核心。

  • BOM核心-window对象
  • 控制窗口及弹窗
  • 通过location对象获取页面信息
  • 通过navigator对象了解浏览器
  • 通过history对象操作浏览器历史

1.window 对象

BOM的核心是window对象,表示浏览器的实例。window对象在浏览器中有两重身份,一个是全局Global对象,另一个是浏览器窗口的JavaScript接口。

1.1 Global 作用域

因为window对象被复用为Global对象,所以通过var申明的所有全局变量和函数都会变成window对象的属性和方法。使用let 和const则不会。

1.2 窗口关系

  • window.top 指向最顶层的窗口对象
  • window.parent 指向当前窗口的父窗口对象。如果当前窗口是最上层窗口,则parent等于top
  • window.self 它是终极window对象,始终指向window

1.3 窗口位置与像素比

  • window.screenLeft 窗口距离屏幕左边的距离
  • window.screenTop 窗口距离屏幕顶部的距离
  • window.moveTo(x, y) 移动窗口到新位置的绝对坐标
  • window.moveBy(x, y) 相对当前位置移动的像素
  • window.devicePixelRatio 设备像素比

将物理像素(屏幕的实际分辨率)转换为CSS像素,举例:手机的物理分辨率为1920x1080,因为像素很小,所以需要将其分辨率降为较低的逻辑分辨率,比如640x320。这个物理像素和CSS像素之间的设备像素比 window.devicePixelRatio 就是3。这样12像素(CSS像素)就是36像素(物理像素)。、

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

// 单位均为CSS像素
window.moveTo(0, 0); // 把窗口移动到左上角
window.moveTo(screen.width, screen.height); // 移动窗口到屏幕右下角
window.moveTo(screen.width / 2, screen.height / 2); // 移动窗口到屏幕中间
window.moveBy(0, 100); // 把窗口向下移动100像素
window.moveBy(-50, 0); // 把窗口向左移动50像素

1.4 窗口大小

  • window.outerWidth 返回浏览器自身的宽度,包括窗口的边框和工具栏
  • window.outerHeight 返回浏览器自身的高度,包括窗口的边框和工具栏
  • window.innerWidth 返回浏览器窗口中页面视口的宽度,不包括窗口的边框和工具栏
  • window.innerHeight 返回浏览器窗口中页面视口的高度,不包括窗口的边框和工具栏
  • document.documentElement.clientWidth 返回页面视口的宽度
  • document.documentElement.clientHeight 返回页面视口的高度
  • document.documentElement.scrollWidth 返回浏览器窗口中页面内容的宽度,包括滚动条
  • document.documentElement.scrollHeight 返回浏览器窗口中页面内容的高度,包括滚动条
  • window.resizeTo(width, height) 调整窗口大小,缩放至对应尺寸
  • window.resizeBy(width, height) 调整窗口大小,缩放对应的大小

1.5 视口位置

  • window.scrollX 文档相对于视口滚动的像素,同window.pageXOffset
  • window.scrollY 文档相对于视口滚动的像素,同window.pageYOffset
  • window.scroll(x, y) 调整窗口滚动位置
  • window.scrollTo(x, y) 调整窗口滚动位置
  • window.scrollBy(x, y) 调整窗口滚动位置
// scroll,scrollTo,scrollBy这三个方法接受相对视口距离的x和y坐标
// 相对于当前视口向下滚动100像素
window.scrollBy(0, 100);
// 相对于当前视口向左滚动50像素
window.scrollBy(-50, 0);
// 滚动到页面左上角
window.scrollTo(0, 0);
// 滚动到距离屏幕左边及顶边各距100像素的位置
window.scrollTo(100, 100);

// 这三个方法也都接受一个ScrollToOptions作为参数,除了提供偏移值,还可以通过behavior属性告诉浏览器是否平滑滚动
window.scrollTo({
  top: 100,
  left: 100,
  behavior: "smooth" // auto
});

1.6 导航与打开新窗口

  • window.open() 返回对一个新建窗口的引用。以下是四个参数

    • 要加载到URL、
    • 目标窗口,如果不是已有窗口则打开一个新的窗口。
    • 特性字符串,指定新窗口的配置。如果打开的不是新窗口,则忽略第三个参数。
    • 表示新窗口在浏览器历史记录中是否替代当前加载页面的布尔值。通常只传前三个参数,最后一个参数只有在不打开新窗口时才使用。
特性字符串 说明
height 数值 新窗口的高度像素,这个值不能小于100。
width 数值 新窗口的宽度像素,这个值不能小于100。
left 数值 新窗口距离窗口左侧的距离 x坐标以像素为单位,不能是负值。
top 数值 新窗口距离窗口顶部的距离,y坐标以像素为单位,不能是负值。
location 'yes'或'no' 是否显示地址栏,设置为no可能禁用地址栏,取决于浏览器
Menubar 'yes'或'no' 是否显示菜单栏,默认为no
resizable 'yes'或'no' 表示是否可以拖动改变窗口大小,默认为no
scrollbars 'yes'或'no' 表示是否显示滚动条,默认为yes
status 'yes'或'no' 表示是否显示状态栏,不同浏览器默认值不一样
toolbar 'yes'或'no' 表示是否显示工具栏,默认为no |
  1. 弹出窗口
// 打开一个新窗口,并导航到指定的URL
window.open("https://www.baidu.com");
// 打开一个新窗口,并导航到指定的URL,并指定新窗口的大小。特性字符串以逗号分隔,键值对出现。
let _blank = window.open("https://www.baidu.com", "_blank", "height=500,width=500");
// 缩放
_blank.resizeTo(500, 500);
// 移动
_blank.moveTo(100, 100);
// 关闭弹窗,只能关闭window.open()打开的弹窗
window.close();
console.log(_blank.closed); // true
  1. 禁止标签间通信
let _blank = window.open("https://www.baidu.com", "_blank", "height=500,width=500");
// 新创建的window对象有一个属性opener,指向打开他的窗口。
console.log(_blank.opener === window); // true
// 某些浏览器里,新标签页会运行在独立的进程中。
_blank.opener = null; // 表示新打开的标签页不需要与打开它的标签页通信。这个连接一旦断开则无法恢复。

3.检测window.open()打开的弹窗是否被屏蔽

// 检测弹窗是否被浏览器屏蔽,检测弹窗是否被屏蔽,不影响浏览器显示关于弹窗被屏蔽的消息。
let blocked = false;
try {
  let _blank = window.open("https://www.baidu.com", "_blank", "height=500,width=500");
  if (_blank == null) {
    blocked = true;
  }
} catch (ex) {
  blocked = true;
}
if (blocked) {
  alert("弹窗被浏览器屏蔽");
}

1.7 定时器

JavaScript 在浏览器中说单线程运行的,启用定时器都会进入任务队列,其中的任务会按添加到队列的先后顺序执行。

  • setTimeout(callback, delay) 一定时间后延迟执行函数或指定代码段,返回一个定时器ID
  • clearTimeout() 取消对应的setTimeout 定时器
  • setInterval(callback, delay) 每隔一段时间执行函数或代码段,返回一个定时器ID
  • clearInterval() 取消对应的setInterval 定时器

使用setInterval实现循环任务

let num = 0,
  intervalId = null;
let max = 10;
let incrementNumber = function () {
  num++;
  // 如果达到最大值,则取消所有未执行的任务
  if (num === max) {
    clearInterval(intervalId);
    alert("执行完毕");
  }
};
intervalId = setInterval(incrementNumber, 500);

使用setInterval实现循环任务 出现的坑

// 如果setInterval计时器的回调函数执行完需要5秒,而计时器时间间隔为3秒,那会发生什么?
function sleep(time) {
  let startTime = window.performance.now();
  while (window.performance.now() - startTime < time) {}
}
let count = 1;
let getTime = window.performance;
let startTime = getTime.now();

setInterval(function () {
  console.log(`第${count}次开始 ${getTime.now() - startTime}`); // 显示开始时间
  sleep(500); // 程序滞留500ms
  console.log(`第${count}次结束 ${getTime.now() - startTime}`); // 显示结束时间
  count += 1;
}, 300); // 300ms间隔

使用setTimeout递归实现循环任务

let num = 0;
let max = 10;
let incrementNumber = function () {
  num++;
  // 如果还没有达到最大值,再设置一个超时任务
  if (num < max) {
    setTimeout(incrementNumber, 500);
  } else {
    alert("执行完毕");
  }
};
setTimeout(incrementNumber, 500);
  1. 使用setTimeout 和 setInterval 设置循环任务的区别
    上面的例子使用setTimeout(),不一定要记录超时ID,因为它会在条件满足时自动停止,否则会自动设置另一个超时任务(如果循环次数不是很庞大,不然还是要记录ID手动清除)。这是设置循环任务的推荐做法,而不是使用setInterval。setInterval很少在生产环境使用,因为下一个任务结束和下一个任务开始之间的时间间隔是无法保证的,有些循环定时任务可能会因此而被跳过。使用setInterval,浏览器只会在时间间隔将任务推入任务队列,而不关心任务什么时候执行或者执行要化多少时间

1.8 系统对话框

以下是系统自带三个对话框,UI框架里封装了这三种常见的对话框

  • alert() 警告框,在用户没有做任何操作之前,会一直显示该对话框,直到用户点击确定按钮。
  • confirm() 确认框,在用户没有做任何操作之前,会一直显示该对话框,直到用户点击确定按钮或者取消按钮。
  • prompt() 提示框,在用户没有做任何操作之前,会一直显示该对话框,直到 用户输入内容并按下确定按钮。

以下两种对话框是异步显示的,这两个方法不会返回用户在对话框里执行的任何操作,所以因此难以利用。

  • find() 查找框,类似于CTRL+F 查找内容并快速定位。
  • print() 打印框,弹出打印列表。
// alert警告框
alert("Hello World");
// confirm确认框
let isOk = confirm("Are you sure?");
if (isOk) {
  alert("Yes");
} else {
  alert("No");
}
// prompt提示框
let name = prompt("Please enter your name:", "");
if (name !== null) {
  alert("Welcome, " + name);
}
// 显示打印对话框
window.print();
// 查询并定位内容
window.find("要查找的内容"); // 找到了返回true,没找到返回false

2.Location 对象

Location 是最有用的BOM对象之一,提供了当前窗口加载文档的信息,它包含了当前文档的 URL 信息,包括 URL 的所有属性和方法。它既是window的属性,也是document的属性,window.location 和 document.location 都指向同一个对象。

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

属性 说明
location.hash "#contents" URL的散列值(井号后跟零或多个字符),如果没有则为空字符串
location.host "www.wrox.com:80" 服务器名及端口号
location.hostname " http://www.wrox.com:80/WileyCDA/?q=javascript#contents" 服务器名
location.href 数值 当前加载页面的完整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 查询字符串

  1. location.search 返回的信息并不能直接使用,通常需要处理。
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
};
// 假设查询字符串为?q=javascript&num=10
let args = getQueryStringArgs();
console.log(args['q']);	// "javascript"
console.log(args['num']);	// "10"
  1. URLSearchParams 通过他们检查和修改查询字符串。
let qs = "?q=javascript&num=10";
let searchParams = new URLSearchParams(qs);

console.log(searchParams.toString()); // "q=javascript&num=10"
searchParams.has("q"); // true
searchParams.get("num"); // "10"

searchParams.set("page", "3");
console.log(searchParams.toString()); // "q=javascript&num=10&page=3"
  1. URLSearchParams 也支持迭代
let qs = "?q=javascript&num=10";
let searchParams = new URLSearchParams(qs);
for (let param of searchParams) {
  console.log(param);
}
// ["q", "javascript"]
// ["num", "10"]
  1. axios自带qs库
import qs from "qs";
const userObj = { firstnName: "paul", lastName: "walker" };
// qs.stringify(),是将对象序列化成url形式的字符串,以&符号进行拼接。
qs.stringify(userObj); // "firstnName=paul&lastName=walker"
// qs.parse(),是将URL形式的字符串解析成对象
qs.parse(userStr); // { firstnName: "paul", lastName: "walker" }

2.2 操作地址

  1. 可以通过修改location对象修改浏览器的地址,以下三种方法location.href 为最常见的。
// 这行代码会立刻导航到新URL地址,同时在浏览器中增加一条记录
location.assign("http://www.wrox.com");
// 以下代码都显式的调用assign()方法
window.location = "http://www.wrox.com";
location.href = "http://www.wrox.com";

除了hash之外,只要修改location对象的任何属性,都会导致页面重新加载到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";
// 把URL修改为http://www.somewhere.com/WileyCDA/
location.hostname = "www.somewhere.com";
// 把URL修改为http://www.somewhere.com/mydirectory
location.pathname = "/mydirectory";
// 把URL修改为http://www.somewhere.com:8080/WileyCDA/
location.port = "8080";
// 把URL修改为https://www.somewhere.com/WileyCDA/
location.protocol = "https:";/
  1. 希望跳转到时候不会增加历史记录,调用replace()方法
location.replace("http://www.wrox.com");
  1. 重新加载当前页面reload()方法,如果页面自从上次请求以来从来没修改过,浏览器可能会从缓存中加载页面
location.reload(); // 重新加载当前页面,可能从缓存加载
location.reload(true); // 重新加载,从服务器加载

3.Navigator对象

Navigator对象包含有关浏览器的信息,它是window对象的属性,它包含了浏览器的各种信息,比如浏览器的名称、版本、平台、用户代理字符串等。

3.1 常用属性

属性名 描述
navigator.activeVrDisplays 返回数组,包含ispresenting属性为true的VRDisplay实例
navigator.appCodeName 返回浏览器的代码名称,非Mozilla浏览器也会返回"Mozilla"
navigator.appName 返回浏览器的名称
navigator.appVersion 返回浏览器的版本,通常与实际版本不一致
navigator.battery 返回暴露Battery Status API的BatteryManager对象
navigator.buildID 返回浏览器的构建ID
navigator.connection 返回暴露Network Information API的NetworkInformation对象
navigator.cookieEnabled 返回是否已启用cookie,布尔值
navigator.credentials 返回暴露Credential Management API的Credentials对象
navigator.deviceMemory 返回单位为GB的设备内存容量
navigator.doNotTrack 返回用户的"不跟踪"(do-not-track)设置
navigator.geolocation 返回暴露Geolocation API的Geolocation对象
navigator.getVRDisplays() 返回数组,包含可用的每个VRDisplay实例
navigator.getUserMedia() 返回与可用媒体设备硬件关联的流
navigator.hardwareConcurrency 返回设备的处理器核心数量
navigator.javaEnabled 返回是否已启用Java,布尔值
navigator.language 返回浏览器的主语言
navigator.languages 返回浏览器偏好的语言数组
navigator.locks 返回暴露 Web Locks API的 LockManager对象
navigator.mediaCapabilities 返回暴露Media Capabilities API的 MediaCapabilities 对象
navigator.mediaDevices 返回可用的媒体设备
navigator.maxTouchPoints 返回设备触摸屏支持的最大触摸点数
navigator.mimeTypes 返回浏览器注册的MIME类型数组
navigator.onLine 返回浏览器是否处于线上状态,布尔值
navigator.oscpu 返回浏览器运行设备的操作系统或CPU
navigator.permissions 返回暴露Permissions API的Permissions对象
navigator.platform 返回浏览器运行的系统平台,通常与实际平台不一致
navigator.plugins 返回浏览器的插件数组
navigator.product 返回产品名称(通常是'Gecko')
navigator.productSub 返回产品额外信息(通常是'Gecko'版本)
navigator.registerProtocolHandler() 将一个网站注册为一个特定协议的处理程序
navigator.requestMediaKeySystemAccess() 返回一个Promise,解决为MediaKeySystemAccess对象
navigator.sendBeacon() 异步传输一些小数据
navigator.serviceWorker 返回ServiceWorker 实例交互的ServiceWorkerContainer
navigator.share() 返回当前平台的原生共享机制
navigator.storage 返回暴露 Storage API的 StorageManager 对象
navigator.userAgent 返回浏览器的用户代理字符串
navigator.vendor 返回浏览器的厂商名称
navigator.vendorSub 返回浏览器的厂商更多信息
navigator.vibrate() 触发设备振动
navigator.webdriver 返回浏览器当前是否被自动化程序控制

3.2 检测插件 pending

通过plugins数组来检测浏览器是否安装了某个插件。

  • name: 插件名称
  • description: 插件介绍
  • filename: 插件文件名
  • length: 由当前插件处理的MIME类型数量
function hasPlugin(name) {
  for (let plugin of navigator.plugins) {
    if (plugin.name.toLowerCase().indexOf(name) > -1) {
      return true;
    }
  }
  return false;
}

// 检查是否安装Flash
hasPlugin("flash"); // false

3.3 注册处理程序

支持navigator.registerProtocolHandler()方法注册一个网站为特定协议的处理程序。随着RSS阅读器和电子邮件客户端的流行。可以借助这个方法把Web应用程序注册为像桌面软件一样的默认应用程序。

navigator.registerProtocolHandler() 方法接受三个参数:

  • scheme: 要注册的协议,如mailto 或 ftp
  • url: 处理程序的URL
  • title: 处理程序的标题
邮件地址就可以通过指定web应用程序打开。
navigator.registerProtocolHandler("mailto", "http://example.com/mailto/?recipient=%s", "Example Mailto Handler");

3.screen对象

screen对象是浏览器窗口外的客户端显示器的信息。比如像素宽度和像素高度。

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

4.History 对象

  • history.go(int) 可以在用户记录中沿任何方向导航,只接受一个参数(整数)
  • history.back() 后退一页
  • history.forward() 前进一页
  • history.length 记录的历史记录,确定用户浏览器的起点是不是你的页面

页面URL发生了变化,历史记录也会生成一条新条目。设置location.hash为新值,历史记录会增加一条。单页面应用程序框架的的哈希模式就是以此来模拟前进和后退,从而不触发导航而页面刷新。

5.历史状态管理

hashchange事件解决的是无需使用前进后退刷新页面的问题,为history对象增加了方便的状态管理特性。haschange 会在页面URL的散列发生变化时被触发,开发者可以在此时执行某些操作。而状态API则可以让开发者改变浏览器URL而不会加载新页面,为此可以使用pushState方法

  • history.pushState(state, title, url) 向历史记录添加一条新条目,并将其设置为当前状态
  • history.replaceState(state, title, url) 向历史记录添加一条新条目,并将其设置为当前状态,但不添加一条新条目
  • history.state 返回当前状态

使用pushState需要保证创建的每个假URL背后对应着服务器上一个真实的物理URL,否则刷新会导致404错误。所有单页面应用程序都必须通过服务器或客户端端某些配置解决这个问题。

posted @   wanglei1900  阅读(14)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示