浏览器对象模型(BOM)
Browser Object Model
==================
window(窗口)
说明
BOM 的核心是 window 对象,表示浏览器的实例。
这意味着网页中定义的所有对象、变量和函数都以 window 作为其 Global 对象,都可以访问其上定义的 parseInt()等全局方法对象不但充当全局作用域,而且表示浏览器窗口。
全局对象
浏览器窗口对象
MDN文档:
https://developer.mozilla.org/zh-CN/docs/Web/API/Window
窗口对象API
窗口关系
1、top 对象始终指向最上层(最外层)窗口,即浏览器窗口本身。
2、 parent 对象则始终指向当前窗口的父窗口。
如果当前窗口是最上层窗口,则 parent 等于 top(都等于 window)。
3、name属性,最上层的 window如果不是通过 window.open()打开的,那么其 name 属性就不会包含值。
4、还有一个 self 对象,它是终极 window 属性,始终会指向 window。实际上,self 和 window 就是同一个对象。
之所以还要暴露 self,就是为了和 top、parent 保持一致。
这些属性都是 window 对象的属性,因此访问 window.parent、window.top 和 window.self都可以。
这意味着可以把访问多个窗口的 window 对象串联起来,比如 window.parent.parent。
窗口位置
依浏览器而定,可能会被部分或全部禁用
screenLeft 和screenTop 属性,用于表示窗口相对于屏幕左侧和顶部的位置 ,返回值的单位是 CSS 像素。
screenLeft //100 单位是像素
window.screenTop // 100 单位是像素,加不加window. 都可以,不加就是往上查找,最终找到window
可以使用 moveTo()和 moveBy()方法移动窗口,参数是xy值,单位是像素
// 把窗口移动到左上角
window.moveTo(0,0);
// 把窗口向下移动 100 像素
window.moveBy(0, 100);
// 把窗口移动到坐标位置(200, 300)
window.moveTo(200, 300);
// 把窗口向左移动 50 像素
window.moveBy(-50, 0);
像素比
CSS 像素是 Web 开发中使用的统一像素单位
定义像素大小是为了在不同设备上统一标准。
比如,低分辨率平板设备上 12 像素(CSS 像素)的文字应该与高清 4K 屏幕下12 像素(CSS 像素)的文字具有相同大小。
这就带来了一个问题,不同像素密度的屏幕下就会有不同的缩放系数,以便把物理像素(屏幕实际的分辨率)转换为 CSS 像素(浏览器报告的虚拟分辨率)。
举个例子,手机屏幕的物理分辨率可能是 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 表示物理像素与逻辑像素之间的缩放系数。
窗口大小
outerWidth 和 outerHeight 返回浏览器窗口自身的大小(不管是在最外层 window 上使用,还是在窗格中使用)。
innerWidth和 innerHeight 返回浏览器窗口中页面视口的大小(不包含浏览器边框和工具栏)。
document.documentElement.clientWidth 和 document.documentElement.clientHeight 返回页面视口的宽度和高度。
设置方法一
<script>
var w = window.innerWidth
|| document.documentElement.clientWidth
|| document.body.clientWidth;
var h = window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight;
var x = document.getElementById("demo");
x.innerHTML = "浏览器内窗宽度:" + w + ",高度:" + h + "。"+:"不包括工具栏和滚动条";
</script>
设置方法二
let pageWidth = window.innerWidth,
pageHeight = window.innerHeight;
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;
}
}
可以使用resizeTo()和resizeBy()方法调整窗口大小。
与移动窗口的方法一样,缩放窗口的方法可能会被浏览器禁用,而且在某些浏览器中默认是禁用的。
同样,缩放窗口的方法只能应用到最上层的 window 对象。
// 缩放到 100×100
window.resizeTo(100, 100);
// 缩放到 200×150
window.resizeBy(200, 150);
// 缩放到 300×300
window.resizeTo(300, 300);
视口位置(滚动)
浏览器窗口尺寸通常无法满足完整显示整个页面,为此用户可以通过滚动在有限的视口中查看文档。
度量文档相对于视口滚动距离的属性有两对,返回相等的值: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);
// 正常滚动
window.scrollTo({
left: 100,
top: 100,
behavior: 'auto'
});
// 平滑滚动
window.scrollTo({
left: 100,
top: 100,
behavior: 'smooth'
});
导航与打开新窗口
window.open(新连接url,新窗口名字name,新窗口属性) - 打开新窗口
// 与<a href="http://www.wrox.com" target="topFrame"/>相同
window.open("http://www.xxx.com/", "topFrame");
//第二个参数也可以是一个特殊的窗口名,比如_self、_parent、_top 或_blank。
第三个参数
操控
这个方法只能用于 window.open()创建的弹出窗口
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);
alert(wroxWin.opener === window); // true
//新创建窗口的 window 对象有一个属性 opener,指向打开它的窗口。
//这个属性只在弹出窗口的最上层 window 对象(top)有定义,是指向调用 window.open()打开它的窗口或窗格的指针
// 设置为 null,表示新打开的标签页可以运行在独立的进程中,无法与其他标签通信
//还可以使用 close()方法像这样关闭新打开的窗口:
wroxWin.close();
// 关闭窗口以后,窗口的引用虽然还在,但只能用于检查其 closed 属性
wroxWin.close();
alert(wroxWin.closed); // true
安全限制
浏览器会在用户操作下才允许创建弹窗。在网页加载过程中调用 window.open()没有效果,而且还可能导致向用户显示错误。
弹窗通常可能在鼠标点击或按下键盘中某个键的情况下才能打开
弹窗屏蔽程序
如果浏览器内置的弹窗屏蔽程序阻止了弹窗,那么 window.open()很可能会返回 null。
let wroxWin = window.open("http://www.wrox.com", "_blank");
if (wroxWin == null){
alert("The popup was blocked!");
}
在浏览器扩展或其他程序屏蔽弹窗时,window.open()通常会抛出错误。
let blocked = false;
try {
let wroxWin = window.open("http://www.wrox.com", "_blank");
if (wroxWin == null){
blocked = true;
}
} catch (ex){
blocked = true;
}
if (blocked){
alert("The popup was blocked!");
}
定时器
JavaScript 在浏览器中是单线程执行的,但允许使用定时器指定在某个时间之后或每隔一段时间就执行相应的代码。
setTimeout()用于指定在一定时间后执行某些代码,
setInterval()用于指定每隔一段时间执行某些代码。
setTimeout 一定时间后执行
// 在 1 秒后显示警告框
setTimeout(() => alert("Hello world!"), 1000);
JavaScript 维护了一个任务队列。其中的任务会按照添加到队列的先后顺序执行。
setTimeout()的第二个参数只是告诉 JavaScript 引擎在指定的毫秒数过后把任务添加到这个队列。
如果队列是空的,则会立即执行该代码。
如果队列不是空的,则代码必须等待前面的任务执行完才能执行。
调用 setTimeout()时,会返回一个表示该超时排期的数值 ID。
这个超时 ID 是被排期执行代码的唯一标识符,可用于取消该任务。
// 设置超时任务
let timeoutId = setTimeout(() => alert("Hello world!"), 1000);
// 取消超时任务
clearTimeout(timeoutId); //在任务执行后再调用clearTimeout()没有效果。
setInterval 每隔一段时间执行
setInterval(() => alert("Hello world!"), 10000);// 间隔时间,每间隔10秒执行弹窗
间隔时间,指的是向队列添加新任务之前等待的时间。
比如,调用 setInterval()的时间为 01:00:00,间隔时间为 3000 毫秒。
这意味着 01:00:03 时,浏览器会把任务添加到执行队列。
浏览器不关心这个任务什么时候执行或者执行要花多长时间。
因此,到了 01:00:06,它会再向队列中添加一个任务。
由此可看出,执行时间短、非阻塞的回调函数比较适合 setInterval()。
setInterval()方法也会返回一个循环定时 ID,可以用于在未来某个时间点上取消循环定时。
相对于 setTimeout()而言,取消定时的能力对 setInterval()更加重要。毕竟,如果一直不管它,那么定时任务会一直执行到页面卸载。
let num = 0, intervalId = null;
let max = 10;
let incrementNumber = function() {
num++;
// 如果达到最大值,则取消所有未执行的任务
if (num == max) {
clearInterval(intervalId);
alert("Done");
}
}
intervalId = setInterval(incrementNumber, 500);
也可以用setTimeout实现:
// 设置循环任务的推荐做法
let num = 0;
let max = 10;
let incrementNumber = function() {
num++;
// 如果还没有达到最大值,再设置一个超时任务
if (num < max) {
setTimeout(incrementNumber, 500);
} else {
alert("Done");
}
}
setTimeout(incrementNumber, 500); // 不一定要记录超时 ID,因为它会在条件满足时自动停止
setIntervale()在实践中很少会在生产环境下使用,因为一个任务结束和下一个任务开始之间的时间间隔是无法保证的,有些循环定时任务可能会因此而被跳过。
而像前面这个例子中一样使用 setTimeout()则能确保不会出现这种情况。
一般来说,最好不要使用 setInterval()。
系统对话框
使用 alert()、confirm()和 prompt()方法,可以让浏览器调用系统对话框向用户显示消息。
这些对话框与浏览器中显示的网页无关,而且也不包含 HTML。
它们的外观由操作系统或者浏览器决定,无法使用 CSS 设置。
此外,这些对话框都是同步的模态对话框,即在它们显示的时候,代码会停止执行,在它们消失以后,代码才会恢复执行。
都是window的方法,因此window可以省略,省略后如果找不到对应的方法,就最终会找到最顶级的window方法。
警告框 alert
通常用于向用户显示一些他们无法控制的消息,比如报错
alert(123) // 只接受1个参数,只有一个ok按钮
确认框 confirm
通常给用户选择
let userSelect = confirm(123) // 确认框有两个按钮:“Cancel”(取消,返回false)和“OK”(确定,返回true)。
提示框 prompt
提示框的用途是提示用户输入消息
let userInput = prompt("显示给用户的内容", "输入框的默认值") // 返回用户输入的内容,点击取消或者关闭对话框会返回null
打印窗口 print
window.print() // 调用打印窗口
查找 find
调用类似ctrl + F 的查找,找到东西就返回ture,找不到就返回false。
因为返回的东西用处不大,因此用得很少。
window.find('要查找的字符串',);
window .find(aString,aCaseSensitive,aBackwards,aWrapAround,aWholeWord,aSearchInFrames,aShowDialog);
aString
要搜索的文本字符串。
aCaseSensitive
布尔值。如果为true,指定区分大小写的搜索。
aBackwards
布尔值。如果为true,指定向后搜索。
aWrapAround
布尔值。如果为true,指定环绕搜索。
aWholeWord 未实现
布尔值。如果为true,指定整个单词搜索。这没有实现;参见bug 481513。
aSearchInFrames
布尔值。如果为true,指定在帧中搜索。
返回
如果找到了字符串,则返回true;否则,返回false。
伪协议
下面更利于SEO优化,而且即使JavaScript被禁用,连接都是可用的
加载顺序
document.onload 》window.onload
常见事件
EventTarget 事件
https://developer.mozilla.org/zh-CN/docs/Web/Events
==================
location 属性(页面路径url)
说明
location 是最有用的 BOM 对象之一,提供了当前窗口中加载文档的信息,以及通常的导航功能。
这个对象独特的地方在于,它既是 window 的属性,也是 document 的属性(window.location 和 document.location 指向同一个对象)。
location 对象不仅保存着当前加载文档的信息,也保存着把 URL 解析为离散片段后能够通过属性访问的信息。
页面路径信息
假设浏览器当前加载的 URL 是 http://foouser:barpassword@www.wrox.com:80/WileyCDA/?q=javascript#contents
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 的源地址。只读
URL的查询字符串
URL 的查询字符串。这个字符串以问号开头,以键值对的形式存在,格式为:键1=值1&键2=值2&…键n=值n
location.search //假设为?q=javascript&num=10
// 可以通过WEB API里提供的 URLSearchParams API,把查询字符串变成URLSearchParams类的实例,方便我们使用
let searchParams = new URLSearchParams(location.search );
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"]
操作URL地址
修改并跳转(有历史记录)
可以通过修改 location 对象修改浏览器的地址
location.assign("https://www.baidu.com"); // 立刻导航到这个新地址,同时在浏览器历史记录中增加一条记录。
// 等同于下面的,都是会调用location.assign()
window.location = "https://www.baidu.com";
location.href = "https://www.baidu.com";
修改 location 对象的属性也会修改当前加载的页面,并且会重新加载。
其中,hash、search、hostname、pathname和 port 属性被设置为新值之后都会修改当前 URL。
上面不管怎么修改,浏览器历史记录中就会增加相应的历史记录。
跳转(无历史记录)
可以使用 replace()方法,不增加历史记录,不能通过浏览器的返回按钮返回。
location.replace("https://www.taobao.com/")
重新加载
它能重新加载当前显示的页面。
调用 reload()而不传参数,页面会以最有效的方式重新加载。也就是说,如果页面自上次请求以来没有修改过,浏览器可能会从缓存中加载页面。
脚本中位于 reload()调用之后的代码可能执行也可能不执行,这取决于网络延迟和系统资源等因素。为此,最好把 reload()作为最后一行代码。
location.reload(); // 重新加载,可能是从缓存加载
location.reload(true); // 重新加载,从服务器加载
console.log('我可能执行也可能不执行')
==================
navigator 属性(浏览器信息)
只要浏览器启用 JavaScript,navigator 对象就一定存在。
对象表示浏览器的信息,最常用的属性包括:
navigator.appName;//浏览器名称;
navigator.appVersion;//浏览器版本;
navigator.language;//浏览器设置的语言;
navigator.platform;//操作系统类型;
navigator.userAgent;//浏览器设定的User-Agent字符串。
navigator.javaEnabled; // 返回布尔值,表示浏览器是否启用了 Java
navigator.vendor;// 返回浏览器的厂商名称,如 "Google Inc."
navigator.cookieEnabled ;//浏览器是否启用cookie
来自 navigator 对象的信息通常是误导性的,不应该用于检测浏览器版本
===================
screen 属性(屏幕)
window 的另一个属性 screen 对象,是为数不多的几个在编程中很少用的 JavaScript 对象。
//属 性 说 明
screen.availLeft //没有被系统组件占用的屏幕的最左侧像素(只读)
screen.availTop //没有被系统组件占用的屏幕的最顶端像素(只读)
screen.availHeight //屏幕像素高度减去系统组件高度(只读)屏幕可用高度
screen.availWidth //屏幕像素宽度减去系统组件宽度(只读)屏幕可用宽度
screen.colorDepth 表示屏幕颜色的位数;多数系统是 32(只读)
screen.pixelDepth //屏幕的位深(只读),现代的话和颜色深度是相等的
screen.height // 屏幕像素高度
screen.width // 屏幕像素宽度
screen.left // 当前屏幕左边的像素距离
screen.top // 当前屏幕顶端的像素距离
screen.orientation // 返回 Screen Orientation API 中屏幕的朝向
================
history(浏览器的历史记录)
说明
因为 history 是 window 的属性,所以每个 window 都有自己的 history 对象。
前进后退
JavaScript可以调用history对象的back()或forward (),相当于用户点击了浏览器的“后退”或“前进”按钮。
出于安全考虑,这个对象不会暴露用户访问过的 URL,但可以通过它在不知道实际 URL 的情况下前进和后退。
这个对象属于历史遗留对象,对于现代Web页面来说,由于大量使用AJAX和页面交互,简单粗暴地调用history.back()可能会让用户感到非常愤怒。
新手开始设计Web页面时喜欢在登录页登录成功时调用history.back(),试图回到登录前的页面。这是一种错误的方法。
任何情况,你都不应该使用history这个对象了。
状态管理 与 单页面应用SPA
而状态管理API 则可以让开发者改变浏览器 URL ,但不刷新页面。
这有什么用?我们改变URL,不就是想要获取新页面吗?
现在类似Vue、React等单页面应用,就是通过这样的方式(路由),就是改变浏览器的URL,通过 JS 获取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);
}
});
基于这个状态,应该把页面重置为状态对象所表示的状态(因为浏览器不会自动为你做这些)。
记住,页面初次加载时没有状态。因此点击“后退”按钮直到返回最初页面时,event.state 会为 null。
可以通过 history.state 获取当前的状态对象,也可以使用 replaceState()并传入与pushState()同样的前两个参数来更新状态。
更新状态不会创建新历史记录,只会覆盖当前状态:
history.replaceState({newFoo: "newBar"}, "New title");
传给 pushState()和 replaceState()的 state 对象应该只包含可以被序列化的信息。
因此,DOM 元素之类并不适合放到状态对象里保存。
注意 使用 HTML5 状态管理时,要确保通过 pushState()创建的每个“假”URL 背后都对应着服务器上一个真实的物理 URL。
否则,单击“刷新”按钮会导致 404 错误。所有单页应用程序(SPA,Single Page Application)框架都必须通过服务器或客户端的某些配置解决这个。