# BOM 对象
# 1. BOM 概述
BOM(Browser Object Model)即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是 window。
BOM 由一系列相关的对象构成,并且每个对象都提供了很多方法与属性。
BOM 缺乏标准,JavaScript 语法的标准化组织是 ECMA,DOM 的标准化组织是 W3C,BOM 最初是Netscape 浏览器标准的一部分。
# 1.2 BOM 的构成
window 对象是浏览器的顶级对象,它具有双重角色。
- 它是 JS 访问浏览器窗口的一个接口。
- 它是一个全局对象。定义在全局作用域中的变量、函数都会变成 window 对象的属性和方法。
在调用的时候可以省略 window,如 alert()、prompt() 等
# 2. window对象
都可以不通过window.访问
# 1. 窗口
关系
- window.parent 当前窗口的父窗口对象,如果没有返回自身
- window.top 顶层窗口对象
- windows.self 自身对象
大小
- outerWidth 和 outerHeight 返回浏览器窗口自身的大小
- innerWidth 和 innerHeight 返回浏览器窗口中页面视口的大小
位置
- window.pageXoffset / window.scrollX 页面水平方向滚动的像素值。
- window.pageYoffset / window.scrollY 页面垂直方向滚动的像素值。
pageXOffset 属性是 scrollX 属性的别名,为了跨浏览器兼容性,请使用 window.pageXOffset 代替 window.scrollX。另外,旧版本的 IE(<9)两个属性都不支持
滚动
- scroll / scrollTo
- scrollBy
这3个方法都接收表示相对 视口距离的和坐标,这两个参数在前两个方法中表示要滚动到的坐标,在最后一个方法中表示滚动的距 离
// 相对于当前视口向右滚动40像素
window.scrollBy(40, 0);
// 滚动到页面左上角
window.scrollTo(0, 0);
// 除了提供偏移值,还可以通过 behavior 属性,告诉浏览器是否平滑滚动。
window.scrollTo({
left: 100,
top: 100,
behavior: 'smooth'
});
2
3
4
5
6
7
8
9
10
11
导航
- window.open()
window.open() 方法可以用于导航到指定URL,也可以用于打开新浏览器窗口。这个方法接收3个参数:要加载的URL、目标窗口、特性字符串,并返回新窗口对象的引用
window.open("http://www.wrox.com/",
"topFrame",
"height=400,width=400,top=10,left=10,resizable=yes");
2
3
如果有一个窗口名叫 "topFrame" ,则这个窗口就会打开这个URL;否 则就会打开一个新窗口并将其命名为 "topFrame" 。第二个参数也可以是一个特殊的窗口名,比如 _self
、_parent
、_top
或 _blank
。
# 2. 常见事件
# 2.1 窗口加载事件
window.onload = function(){}
//或者
window.addEventListener("load",function(){});
2
3
window.onload
是窗口 (页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS 文件等), 就调用的处理函数。
注意:
- 有了
window.onload
就可以把 JS 代码写到页面元素的上方,因为onload
是等页面内容全部加载完毕,再去执行处理函数。
2.window.onload
传统注册事件方式 只能写一次,如果有多个,会以最后一个 window.onload
为准。
- 如果使用
addEventListener
则没有限制。
document.addEventListener('DOMContentLoaded',function(){})
DOMContentLoaded
事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等。IE9以上才支持- 如果页面的图片很多的话, 从用户访问到
onload
触发可能需要较长的时间, 交互效果就不能实现,必然影响用户的体验,此时用DOMContentLoaded
事件比较合适。
# 2.2 调整窗口大小事件
window.onresize = function(){}
window.addEventListener("resize",function(){});
2
3
window.onresize
是调整窗口大小加载事件, 当触发时就调用的处理函数。
注意:
只要窗口大小发生像素变化,就会触发这个事件。
我们经常利用这个事件完成响应式布局。
window.innerWidth
当前屏幕的宽度
# 3. 定时器
- setTimeout() 定时器
setTimeout(调用函数, [延迟的毫秒数]);
- 停止 setTimeout() 定时器
clearTimeout(timeoutID)
//里面的参数就是定时器的标识符 。
2
- setInterval() 定时器
setInterval() 方法重复调用一个函数,每隔这个时间,就去调用一次回调函数。
setInterval(回调函数, [间隔的毫秒数]);
- 停止 setInterval() 定时器
clearInterval(intervalID);
//里面的参数就是定时器的标识符 。
2
所有超时执行的代码(函数)都会在全局作用域中的一个匿名函数中运行,因此函数中的 this 值在非严格模式下始终指向 window ,而在严格模式下是 undefined 。如果给 setTimeout() 提供了一个箭头函数,那么 this 会保留为定义它时所在的词汇作用域。
# 3. location 对象
# 3.1 什么是 location 对象
location 是最有用的BOM对象之一,提供了当前窗口中加载文档的信息,以及通常的导航功能。 这个对象独特的地方在于,它既是 window 的属性,也是 document 的属性。也就是说, window.location 和 document.location 指向同一个对象。
location 对象不仅保存着当前加载文档的信息,也保存着把URL解析为离散片段后能够通过属性访问的信息。
# 3.2 location 对象的属性
// 当前网址为
// http://user:passwd@www.example.com:4097/path/a.html?x=111#part1
document.location.href // 整个url
// "http://user:passwd@www.example.com:4097/path/a.html?x=111#part1"
document.location.protocol //当前 URL 的协议,包括冒号
// "http:"
document.location.host //主机。如果端口不是协议默认的80和433,则还会包括冒号(:)和端口
// "www.example.com:4097"
document.location.hostname //主机名,不包括端口
// "www.example.com"
document.location.port //端口号
// "4097"
document.location.pathname //URL 的路径部分
// "/path/a.html"
document.location.search // 查询字符串部分
// "?x=111"
document.location.hash //hash
// "#part1"
document.location.username
// "user"
document.location.password
// "passwd"
document.location.origin //URL的协议、主机名和端口
// "http://user:passwd@www.example.com:4097"
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
这些属性里面,只有origin
属性是只读的,其他属性都可写。
注意,如果对 Location.href
写入新的 URL 地址,浏览器会立刻跳转到这个新地址。
这个特性常常用于让网页自动滚动到新的锚点。
document.location.href = '#top';
// 等同于
document.location.hash = '#top';
2
3
直接改写location,相当于写入href属性。
document.location = 'http://www.example.com';
// 等同于
document.location.href = 'http://www.example.com';
2
3
# 3.3 location 对象的方法
# 3.4 查询字符串
虽然 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;
}
// 假设查询字符串为?q=javascript&num=10
let args = getQueryStringArgs();
alert(args["q"]); // "javascript"
alert(args["num"]); // "10"
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# URLSearchParam
URLSearchParams 对象 (opens new window)
URLSearchParams 提供了一组标准API方法,通过它们可以检查和修改查询字符串。给 URLSearchParams 构造函数传入一个查询字符串,就可以创建一个实例。这个实例上暴露了 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"
2
3
4
5
6
7
8
9
大多数支持 URLSearchParams 的浏览器也支持将 URLSearchParams 的实例用作可迭代对象
let qs = "?q=javascript&num=10";
let searchParams = new URLSearchParams(qs);
for (let param of searchParams) {
console.log(param);
}
// ["q", "javascript"]
// ["num", "10"]
2
3
4
5
6
7
# 3.5 URL对象
# URL 的编码和解码
网页的 URL 只能包含合法的字符,其他需要进行转义,JavaScript 提供四个 URL 的编码/解码方法。
- encodeURI() 转码整个 URL
- encodeURIComponent() 转码 URL 的组成部分
- decodeURI() 整个 URL 的解码
- decodeURIComponent() URL 片段的解码
encodeURI('http://www.example.com/q=春节')
// "http://www.example.com/q=%E6%98%A5%E8%8A%82"
decodeURI('http://www.example.com/q=%E6%98%A5%E8%8A%82')
// "http://www.example.com/q=春节"
encodeURIComponent('春节')
// "%E6%98%A5%E8%8A%82"
decodeURIComponent('%E6%98%A5%E8%8A%82')
// "春节"
2
3
4
5
6
7
8
9
# URL 接口
浏览器原生提供URL()接口,它是一个构造函数,用来构造、解析和编码 URL。一般情况下,通过window.URL可以拿到这个构造函数。
var url = new URL('http://www.example.com/index.html');
url.href // "http://www.example.com/index.html"
2
URL 实例的属性与Location对象的属性基本一致,返回当前 URL 的信息。
- URL.href:返回整个 URL
- URL.protocol:返回协议,以冒号:结尾
- URL.hostname:返回域名
- URL.host:返回域名与端口,包含:号,默认的80和443端口会省略
- URL.port:返回端口
- URL.origin:返回协议、域名和端口
- URL.pathname:返回路径,以斜杠/开头
- URL.search:返回查询字符串,以问号?开头
- URL.searchParams:返回一个URLSearchParams实例,该属性是Location对象没有的
- URL.hash:返回片段识别符,以井号#开头
- URL.password:返回域名前面的密码
- URL.username:返回域名前面的用户名
静态方法:
- URL.createObjectURL() 方法用来为上传/下载的文件、流媒体文件生成一个 URL 字符串。这个字符串代表了 File 对象或 Blob 对象的 URL。
- URL.revokeObjectURL() 方法用来释放 URL.createObjectURL() 方法生成的 URL 实例。它的参数就是URL.createObjectURL() 方法返回的 URL 字符串。
https://www.yuque.com/mewcoder/blog/ti89tv
# 4. navigator 对象
navigator 对象包含有关浏览器的信息,它有很多属性,我们最常用的是 userAgent
,该属性可以返回由客户机发送服务器的 user-agent
头部的值。
//下面前端代码可以判断用户那个终端打开页面,实现跳转
if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
window.location.href = ""; //手机
} else {
window.location.href = ""; //电脑
}
2
3
4
5
6
7
# 5. history 对象
window 对象给我们提供了一个 history 对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的 URL。
# 历史状态管理
History.pushState() (opens new window)
这个方法接收3个参数:一个 state 对象、一个新状态的标题和一个(可选的)相对URL 。
var stateObj = { foo: 'bar' };
history.pushState(stateObj, 'page 2', '2.html');
2
添加新记录后,浏览器地址栏立刻显示example.com/2.html,但并不会跳转到2.html,甚至也不会检查2.html是否存在,它只是成为浏览历史中的最新记录。这时,在地址栏输入一个新的地址(比如访问google.com),然后点击了倒退按钮,页面的 URL 将显示2.html;你再点击一次倒退按钮,URL 将显示1.html。
总之,pushState()方法不会触发页面刷新,只是导致 History 对象发生变化,地址栏会有反应。
每当同一个文档的浏览历史(即history对象)出现变化时,就会触发popstate事件。
window.addEventListener('popstate', function(event) {
console.log('location: ' + document.location);
console.log('state: ' + JSON.stringify(event.state));
});
2
3
4
注意,仅仅调用pushState()方法或replaceState()方法 ,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用 JavaScript 调用History.back()、History.forward()、History.go()方法时才会触发。另外,该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发。
使用HTML5状态管理时,要确保通过 pushState() 创建的每个“假”URL背后都对应着服务器 上一个真实的物理URL。否则,单击“刷新”按钮会导致404错误。所有单页应用程序(SPA,Single Page Application)框架都必须通过服务器或客户端的某些配置解决这个问题。
# 8.存储
# Cookie
https://www.wangdoc.com/javascript/bom/cookie.html
Cookie 是服务器保存在浏览器的一小段文本信息,一般大小不能超过4KB。浏览器每次向服务器发出请求,就会自动附上这段信息。
document.cookie属性返回当前网页的 Cookie。
document.cookie是可以写的,一次只能写入一个 Cookie,而且写入并不是覆盖,而是添加
document.cookie // "id=foo;key=bar"
HTTP响应设置Cookie
Set-Cookie:foo=bar
如果Set-Cookie字段没有指定Expires或Max-Age属性,那么这个 Cookie 就是 Session Cookie,即它只在本次对话存在,一旦用户关闭浏览器,浏览器就不会再保留这个 Cookie。
删除一个现存 Cookie 的唯一方法,是设置它的expires属性为一个过去的日期。
一些属性:
- Expires 到期时间,UTC格式,
- Max-Age 秒数,如果同时指定了Expires和Max-Age,那么Max-Age的值将优先生效。
- Domain 属性指定 Cookie 属于哪个域名,Domain 属性只能是当前域名或者当前域名的上级域名,但设为上级域名时,不能设为顶级域名(.com)或公共域名(github.io)
- Path 属性指定浏览器发出 HTTP 请求时,哪些路径要附带这个 Cookie。只要浏览器发现,Path属性是 HTTP 请求路径的开头一部分就会在头信息里面带上这个 Cookie。
- Secure 属性指定浏览器只有在加密协议 HTTPS 下,才能将这个 Cookie 发送到服务器。
- HttpOnly属 性指定该 Cookie 无法通过 JavaScript 脚本拿到,主要是document.cookie属性、XMLHttpRequest 对象和 Request API 都拿不到该属性
- SameSite 用来防止 CSRF 攻击和用户追踪。
浏览器向服务器发送 HTTP 请求时,每个请求都会带上相应的 Cookie
Cookie: foo=bar
# sessionStorage
1、生命周期为关闭浏览器窗口
2、在同一个窗口(页面)下数据可以共享
3、以键值对的形式存储使用
//存储数据:
sessionStorage.setItem(key, value)
//获取数据:
sessionStorage.getItem(key)
//删除数据:
sessionStorage.removeItem(key)
//删除所有数据:
sessionStorage.clear()
2
3
4
5
6
7
8
9
10
11
# localStorage
1、声明周期永久生效,除非手动删除 否则关闭页面也会存在
2、可以多窗口(页面)共享(同一浏览器可以共享)
3、以键值对的形式存储使用
//存储数据:
localStorage.setItem(key, value)
//获取数据:
localStorage.getItem(key)
//删除数据:
localStorage.removeItem(key)
//删除所有数据:
localStorage.clear()
2
3
4
5
6
7
8
9
10
11
不同浏览器给 localStorage 和 sessionStorage 设置了不同的空间限制,但大多数会限制为每个域名 5MB。
# IndexedDB
Indexed Database API 简称 IndexedDB,是浏览器中存储结构化数据的一个方案。IndexedDB 用于代 替目前已废弃的 Web SQL Database API。IndexedDB 背后的思想是创造一套 API,方便 JavaScript 对象的 存储和获取,同时也支持查询和搜索。
IndexedDB 的设计几乎完全是异步的。为此,大多数操作以请求的形式执行,这些请求会异步执行, 产生成功的结果或错误。绝大多数 IndexedDB 操作要求添加 onerror 和 onsuccess 事件处理程序来确定输出。
IndexedDB 数据库就是在一个公共命名空间下的一组对 象存储,类似于 NoSQL 风格的实现。
# 9.网页定位
# 1. offset
# 1.1 offset 概述
offset 翻译过来就是偏移量, 我们使用 offset 系列相关属性可以动态的得到该元素的位置(偏移)、大小等。
- 获得元素距离带有定位父元素的位置
- 获得元素自身的大小(宽度高度)
- 注意: 返回的数值都不带单位
offset 系列常用属性:
# 1.2 offset 与 style 区别
# 2. client
client 翻译过来就是客户端,我们使用 client 系列的相关属性来获取元素可视区的相关信息。通过 client 系列的相关属性可以动态的得到该元素的边框大小、元素大小等。
# 3.scroll
scroll 翻译过来就是滚动的,我们使用 scroll 系列的相关属性可以动态的得到该元素的大小、滚动距离等。
如果浏览器的高(或宽)度不足以显示整个页面时,会自动出现滚动条。当滚动条向下滚动时,页面上面被隐藏掉的高度,我们就称为页面被卷去的头部。滚动条在滚动时会触发 onscroll 事件。
# 页面被卷去的头部兼容性解决方案
需要注意的是,页面被卷去的头部,有兼容性问题,因此被卷去的头部通常有如下几种写法:
- 声明了 DTD,使用 document.documentElement.scrollTop
- 未声明 DTD,使用 document.body.scrollTop
- 新方法 window.pageYOffset 和 window.pageXOffset,IE9 开始支持
function getScroll() {
return {
left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft||0,
top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
};
}
// 使用的时候 getScroll().left
2
3
4
5
6
7
# 3.总结
主要用法:
- offset系列 经常用于获得元素位置
offsetLeft
offsetTop
- 经常用于获取元素大小
clientWidth
clientHeight
- 经常用于获取滚动距离
scrollTop
scrollLeft
- 注意页面滚动的距离通过
window.pageXOffset
获得
# 10.mouseenter 和mouseover的区别
当鼠标移动到元素上时就会触发 mouseenter
事件
类似 mouseover
,它们两者之间的差别是:
mouseover
鼠标经过自身盒子会触发,经过子盒子还会触发。mouseenter
只会经过自身盒子触发- 之所以这样,就是因为
mouseenter
不会冒泡 - 鼠标离开
mouseleave
同样不会冒泡
对于深层次结构,发送的 mouseover事件数量可能非常大并且会导致严重的性能问题。在这种情况下,最好是监听 mouseenter 事件。
# 11.拖拽/键盘移动 div
# 拖拽移动div
https://code.h5jun.com/fofev/edit?html,css,js,output (opens new window)
<div id="box">
<style>
*{
margin: 0;
padding: 0;
}
body{
position: relative;
}
#box{
width: 50px;
height: 50px;
background: deepskyblue;
position: absolute;
left: 100px;
top: 50px;
}
</style>
const box = document.getElementById("box");
let nowW, nowH, flag;
box.onmousedown = function (e) {
nowW = e.clientX;
nowH = e.clientY;
flag = true;
document.onmousemove = function (e) {
if (!flag) return false;
const moveX = e.clientX - nowW;
const moveY = e.clientY - nowH;
const left = parseInt(box.style.left || 0);
const top = parseInt(box.style.top || 0);
box.style.left = left + moveX + "px";
box.style.top = top + moveY + "px";
nowW = e.clientX;
nowH = e.clientY;
};
document.onmouseup = function () {
flag = false;
};
document.onmouseleave = function () {
flag = false;
};
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 键盘移动div
https://code.h5jun.com/kehum/edit?html,css,js,output (opens new window)
<body>
<div id="box">
<div id="move">静止</div>
</div>
<script>
const box = document.getElementById("box");
const move = document.getElementById("move");
let lefts = box.style.left || 0;
let tops = box.style.top || 0;
document.addEventListener("keydown", function (e) {
const code = e.keyCode;
move.innerHTML = "开始移动";
switch (code) {
case 38:
move.innerHTML = "上";
tops -= 5;
break;
case 40:
move.innerHTML = "下";
tops += 5;
break;
case 37:
move.innerHTML = "左";
lefts -= 5;
break;
case 39:
move.innerHTML = "右";
lefts += 5;
break;
default:
break;
}
box.style.top = tops + "px";
box.style.left = lefts + "px";
});
document.addEventListener("keyup", function () {
move.innerHTML = "静止";
});
document.addEventListener("keypress", function () {
console.log("keypress");
});
</script>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
← DOM 事件