# BOM 对象

# 1. BOM 概述

BOM(Browser Object Model)即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是 window。

BOM 由一系列相关的对象构成,并且每个对象都提供了很多方法与属性。

BOM 缺乏标准,JavaScript 语法的标准化组织是 ECMA,DOM 的标准化组织是 W3C,BOM 最初是Netscape 浏览器标准的一部分。

img

# 1.2 BOM 的构成

img

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'
});
1
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");
1
2
3

如果有一个窗口名叫 "topFrame" ,则这个窗口就会打开这个URL;否 则就会打开一个新窗口并将其命名为 "topFrame" 。第二个参数也可以是一个特殊的窗口名,比如 _self_parent_top_blank

# 2. 常见事件

# 2.1 窗口加载事件

window.onload = function(){}
//或者 
window.addEventListener("load",function(){});
1
2
3

window.onload 是窗口 (页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS 文件等), 就调用的处理函数。

注意:

  1. 有了window.onload就可以把 JS 代码写到页面元素的上方,因为 onload是等页面内容全部加载完毕,再去执行处理函数。

2.window.onload传统注册事件方式 只能写一次,如果有多个,会以最后一个 window.onload为准。

  1. 如果使用 addEventListener 则没有限制。
document.addEventListener('DOMContentLoaded',function(){})
1
  • DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等。IE9以上才支持
  • 如果页面的图片很多的话, 从用户访问到onload触发可能需要较长的时间, 交互效果就不能实现,必然影响用户的体验,此时用 DOMContentLoaded事件比较合适。

# 2.2 调整窗口大小事件

 window.onresize = function(){}

 window.addEventListener("resize",function(){});
1
2
3

window.onresize是调整窗口大小加载事件, 当触发时就调用的处理函数。

注意:

  1. 只要窗口大小发生像素变化,就会触发这个事件。

  2. 我们经常利用这个事件完成响应式布局。window.innerWidth当前屏幕的宽度

# 3. 定时器

  • setTimeout() 定时器
setTimeout(调用函数, [延迟的毫秒数]);
1
  • 停止 setTimeout() 定时器
clearTimeout(timeoutID)
//里面的参数就是定时器的标识符 。
1
2
  • setInterval() 定时器

setInterval() 方法重复调用一个函数,每隔这个时间,就去调用一次回调函数。

setInterval(回调函数, [间隔的毫秒数]);
1
  • 停止 setInterval() 定时器
clearInterval(intervalID);
//里面的参数就是定时器的标识符 。
1
2

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

# 3. location 对象

# 3.1 什么是 location 对象

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

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

# 3.2 location 对象的属性

img

// 当前网址为
// 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"
1
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';
1
2
3

直接改写location,相当于写入href属性。

document.location = 'http://www.example.com';
// 等同于
document.location.href = 'http://www.example.com';
1
2
3

# 3.3 location 对象的方法

img

# 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"
1
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"
1
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"]
1
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')
// "春节"
1
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"
1
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 = "";     //电脑
 }
1
2
3
4
5
6
7

# 5. history 对象

window 对象给我们提供了一个 history 对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的 URL。

img

# 历史状态管理

History.pushState() (opens new window)

这个方法接收3个参数:一个 state 对象、一个新状态的标题和一个(可选的)相对URL 。

var stateObj = { foo: 'bar' };
history.pushState(stateObj, 'page 2', '2.html');
1
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));
});
1
2
3
4

注意,仅仅调用pushState()方法或replaceState()方法 ,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用 JavaScript 调用History.back()、History.forward()、History.go()方法时才会触发。另外,该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发。

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

# 8.存储

https://www.wangdoc.com/javascript/bom/cookie.html

Cookie 是服务器保存在浏览器的一小段文本信息,一般大小不能超过4KB。浏览器每次向服务器发出请求,就会自动附上这段信息。

document.cookie属性返回当前网页的 Cookie。

document.cookie是可以写的,一次只能写入一个 Cookie,而且写入并不是覆盖,而是添加

document.cookie // "id=foo;key=bar"
1

HTTP响应设置Cookie

Set-Cookie:foo=bar
1

如果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
1

# sessionStorage

1、生命周期为关闭浏览器窗口

2、在同一个窗口(页面)下数据可以共享

3、以键值对的形式存储使用

//存储数据:
sessionStorage.setItem(key, value)

//获取数据:
sessionStorage.getItem(key)

//删除数据:
sessionStorage.removeItem(key)

//删除所有数据:
sessionStorage.clear()
1
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()
1
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 系列常用属性:

img

img

# 1.2 offset 与 style 区别

img

# 2. client

client 翻译过来就是客户端,我们使用 client 系列的相关属性来获取元素可视区的相关信息。通过 client 系列的相关属性可以动态的得到该元素的边框大小、元素大小等。

img

img

# 3.scroll

scroll 翻译过来就是滚动的,我们使用 scroll 系列的相关属性可以动态的得到该元素的大小、滚动距离等。

img

img

如果浏览器的高(或宽)度不足以显示整个页面时,会自动出现滚动条。当滚动条向下滚动时,页面上面被隐藏掉的高度,我们就称为页面被卷去的头部。滚动条在滚动时会触发 onscroll 事件。

# 页面被卷去的头部兼容性解决方案

需要注意的是,页面被卷去的头部,有兼容性问题,因此被卷去的头部通常有如下几种写法:

    1. 声明了 DTD,使用 document.documentElement.scrollTop
    1. 未声明 DTD,使用 document.body.scrollTop
    1. 新方法 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
1
2
3
4
5
6
7

# 3.总结

img

主要用法:

  • 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;
    };
  };
1
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

https://blog.csdn.net/weixin_48989252/article/details/108238686?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.base&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.base (opens new window)

# 键盘移动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>
1
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