6
一、BOM概述
- 概念:BOM即浏览器对象模型
- 通常情况下如果提到了BOM,一般指的是window对象
1.1简介
- BOM提供了独立于内容,而与浏览器窗口进行交互的对象
- BOM由一系列相关对象构成,并且每个对象都提供了很多方法与属性
- BOM缺乏一个统一的标准
- JavaScript语法的标准化组织是ECMA
- DOM的标准化组织是W3C[所有浏览器公共遵守的标准]
- BOM是各个浏览器厂商根据dom在各自浏览器上实现的[表现为不同浏览器定义有差别,实现方式不同]
1.2 BOM和DOM之间的关系:
在说明BOM和DOM之间的关系之前,先来看看如下这张图:
再来看一下如下这张图:
结论:
- DOM通过document对象来访问、控制、修改html和xhtml等文档中的内容
- BOM通过window对象来访问、控制、修改浏览器中的内容
联系:
- BOM包含DOM
- 浏览器提供用来访问的是BOM对象
- 从BOM对象可以访问到DOM对象,从而使javascript可以操作浏览器、并通过操作浏览器读取到文档的内容
区别:
- DOM描述了处理网页内容的方法和接口,即操作页面内部
- BOM描述了与浏览器进行交互的方法和接口,即操作页面之间
二、window对象
2.1 简介
- 描述:window对象可以理解为浏览器对象
- window对象的BOM的具象化表示形式
- window和bom之间的关系就好比document对象和dom之间的关系一样:
bom相当于dom
window对象相当于document对象
注意:
- 因为window对象是js中的顶级对象,因此所有定义在全局作用域中的变量、函数都会变成window对象的属性和方法,在调用的时候可以省略window
这里通过下面的几个示例来演示:
<script>
open('http://www.baidu.com');
</script>
此时当我们打开浏览器时,会发现会多打开一个窗口:
<script>
open('http://www.baidu.com');
window.close();
</script>
此时当关闭之前打开的“百度”窗口,在“window对象”窗口上进行刷新后便会出现如下结果:
<script>
var div = window.document.querySelector('div');
console.log(window.event);
var num;
window.num = 10;
window.console.log(window.num);
</script>
结果如下:
function test() {
console.log('hello world');
}
window.test();
2.2 window对象的name属性
- window.name是window对象的一个属性,默认值为空
- window.name值在不同页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的name值(2MB左右)
- 描述:是页面在切换之后,甚至域名更改之后仍然会存储信息的容器
应用:
- 正是由于window.name属性拥有在不同页面保持存在的特性,因此出现了一门叫做【跨域传输】的技巧
- 而这个技巧的内部实现原理就是window.name的持久性的特性
说明:
- 借助window.name可以实现页面之间数据的传递,称为跨域传输。
如果想要获取一个页面内的信息,那么必须先加载
//page.html里的内容
<script>
var num=10;
window.name='var num=[1,2,3];';
</script>
<button>跨域传输</button>
<script>
//如果想要获取一个页面内的信息,那么必须先加载
var but = document.querySelector('button');
but.onclick = function() {
var iframe = document.createElement('iframe');
iframe.src = 'page.html'; //加载保存了信息的页面
iframe.style.display = 'none';
document.body.appendChild(iframe);
//当iframe加载完毕,意味着window.name的内容已经被赋予完毕
iframe.onload = function(eve) {
var iframeWindowName = eve.target.contentWindow.name;
//console.log(iframeWindowName);
eval(iframeWindowName); //将字符串解析成代码
console.log(num);
}
}
</script>
最后结果如下:
2.3 window尺寸属性
2.3.1 window.outerHeight/outerWidth属性
- 描述:这两个属性返回的是整个浏览器的高度和宽度和页面窗口的大小没有任何关系
<script>
console.log('outerHeight:' + window.outerHeight);
console.log('outerWidth:' + window.outerWidth);
</script>
2.3.2 window.innerHeight/innerWidth属性
- 描述:返回视口的宽高 (计算滚动条的高度) 页面变化它就变
- IE9及其以上
<script>
console.log('innerHeight:' + window.innerHeight);
console.log('innerWidth:' + window.innerWidth);
</script>
2.3.3 document.documentElement.clientHeight/clientWidth属性
- 描述:返回视口的宽高(不计算滚动条的高度)
- 可用于IE9以下的浏览器的标准模式中获取
<script>
console.log('clientHeight:' + document.documentElement.clientHeight);
console.log('clientWidth:' + document.documentElement.clientWidth);
</script>
兼容性处理获取网页宽高:
let {width, height} = getScreen();
console.log(width);
console.log(height);
function getScreen() {
let width, height;
if(window.innerWidth){
width = window.innerWidth;
height = window.innerHeight;
}else if(document.compatMode === "BackCompat"){
width = document.body.clientWidth;
height = document.body.clientHeight;
}else{
width = document.documentElement.clientWidth;
height = document.documentElement.clientHeight;
}
return {
width: width,
height: height
}
}
2.3.4 window.pageYOffset/pageXOffset属性
- 描述:返回页面滚动的距离(通用),这两个属性指的是页面发生滚动的距离
<script>
console.log('pageYOffset:' + window.pageYOffset);
console.log('pageXOffset:' + window.pageXOffset);
</script>
2.3.5 window.screenX/screenY属性
等价于 window.screenLeft/window.screenTop
- 描述:表示页面左上角距离屏幕左上角的横纵坐标
<script>
console.log('screenX:' + window.screenX);
console.log('screenY:' + window.screenY);
</script>
2.3.6 window.navigator属性
- 描述:window.navigator对象包含大量有关Web浏览器的信息,通过navigator我们就能判断用户当前是什么浏览器
<script>
const agent = window.navigator.userAgent;
if (/chrome/i.test(agent)) {
console.log("当前是谷歌浏览器");
} else if (/firefox/i.test(agent)) {
console.log("当前是火狐浏览器");
} else if (/msie/i.test(agent)) {
console.log("当前是低级IE浏览器");
} else if ("ActiveXObject" in window) {
console.log("当前是高级IE浏览器");
}
</script>
2.3.7location属性
描述:代表浏览器地址栏的信息,通过location我们就能设置或获取当前地址信息
<button id="btn1">获取</button>
<button id="btn2">设置</button>
<button id="btn3">刷新</button>
<button id="btn4">强制刷新</button>
const oBtn1 = document.querySelector("#btn1");
const oBtn2 = document.querySelector("#btn2");
const oBtn3 = document.querySelector("#btn3");
const oBtn4 = document.querySelector("#btn4");
//获取当前地址栏的地址
oBtn1.onclick = function() {
console.log(window.location.href);
};
//设置当前地址栏的地址
oBtn2.onclick = function() {
window.location.href = "https:www.baidu.com";
};
//重新加载界面
oBtn3.onclick = function() {
window.location.reload();
};
//重新加载界面
oBtn4.onclick = function() {
window.location.reload(true);
};
2.3.8 history属性
描述:代表浏览器的历史信息,通过history来实现刷新/上一步/下一步
- (1)window.history.back() 跳转到栈中的上一个页面
- (2)window.history.forward() 跳转到栈中的下一个页面
- (3)window.history.go(num) 跳转到栈中的指定页面,num为正表示前进多少个界面;num为负表示后退多少个界面
- (4)window.history.length 栈中页面的数量
注意:
- a.通过window.history对象中提供的方法进行的页面跳转并不会向栈中添加新的页面。
- 通过window.location.href或者通过a标签进行的跳转,则会向栈中添加新的页面。
- b.栈区特征(后进先出),不仅仅意味着后进来的内容先被移除栈,还意味着栈中的内容如果想要添加到指定位置,必须先将之前的内容退栈才行。
具体示例参考:window.history对象小案例
2.3.9 offsetWidth和offsetHeight
获取的宽高包含 边框 + 内边距 + 元素宽高
即可以获取行内设置的宽高也可以获取CSS设置的宽高
只支持获取, 不支持设置
高级低级浏览器都支持
var oDiv = document.getElementById("box");
// oDiv.offsetWidth = "166px";
// oDiv.offsetHeight = "166px";
oDiv.style.width = "166px";
oDiv.style.height = "166px";
console.log(oDiv.offsetWidth);
console.log(oDiv.offsetHeight);
2.3.10 offsetLeft和offsetTop
offsetLeft和offsetTop作用:
获取元素到第一个定位祖先元素之间的偏移位
如果没有祖先元素是定位的, 那么就是获取到body的偏移位
2.3.11 offsetParent
获取元素的第一个定位祖先元素
如果没有祖先元素是定位的, 那么就是获取到的就是body
* {
margin: 0;
padding: 0;
}
.grand-father {
width: 300px;
height: 300px;
margin-top: 100px;
margin-left: 100px;
background: deeppink;
overflow: hidden;
position: relative;
}
.father {
width: 200px;
height: 200px;
margin-top: 100px;
margin-left: 100px;
background: blue;
overflow: hidden;
position: relative;
}
.son {
width: 100px;
height: 100px;
margin-top: 100px;
margin-left: 100px;
background: red;
}
<div class="grand-father">
<div class="father">
<div class="son"></div>
</div>
</div>
let oSDiv = document.querySelector(".son");
oSDiv.onclick = function() {
console.log(oSDiv.offsetParent);
}
2.3.12 clientWidth/clientHeight
offsetWidth = 宽度 + 内边距 + 边框
offsetHeight = 高度 + 内边距 + 边框
clientWidth = 宽度 + 内边距
clientHeight = 高度 + 内边距
offsetLeft/offsetTop: 距离第一个定位祖先元素偏移位 || body
clientLeft/clientTop: 左边框 和 顶部边框
2.3.13 scroll属性
内容没有超出元素范围时
scrollWidth: = 元素宽度 + 内边距宽度 == clientWidth
scrollHeight: = 元素高度 + 内边距的高度 == clientHeight
内容超出元素范围时
scrollWidth: = 元素宽度 + 内边距宽度 + 超出的宽度
scrollHeight: = 元素高度 + 内边距的高度 + 超出的高度
scrollTop / scrollLeft
scrollTop: 超出元素内边距顶部的距离
scrollLeft: 超出元素内边距左边的距离
scrollX:获取横向滚动条的滚动距离
scrollY:获取竖向滚动条的滚动
2.14 window.open(了解)
window.open(“新窗口中的网页地址”,“窗口名称”,“窗口的风格”)
新打开一个窗口,可以设置新窗口的大小、位置等等
2.15 window对象涉及到的一些方法
alert():
- 描述:表示警示框,作用是提示用户信息
- 语法:alert(string)/window.alert(string)
** 注意:**
- 该方法执行后无返回值
- alert()方法弹出的对话框是模态对话框,在对话框关闭之前程序暂停,直到关闭后才继续执行
<button>点我</button>
<script>
document.querySelector('button').onclick = function() {
var result = alert('提示信息');
console.log(result);
}
</script>
当点击“点我”按钮后:
prompt():
- 描述:用来收集用户的信息
- 语法: prompt(alertMsg,defaultMsg);
注意:
- a)点击取消,返回值为null
- b)没有默认值: 如果用户没有输入内容,返回一个空字符串;如果用户输入了内容,返回值为用户输入的内容
- c)有默认值:如果用户没有输入内容,返回默认值; 如果用户修改了默认,返回值为用户输入的内容
- d)prompt()方法弹出的对话框是模态对话框,在对话框关闭之前程序暂停,知道直到后才继续执行
<script>
document.querySelector('button').onclick = function() {
var result = prompt('中国技术哪家强?');
console.log(result);
}
</script>
当点击“点我”按钮后:
输入内容并点击“确定”后:
confirm():
- 描述:作用是提示用户信息
- 语法:confirm(alertMsg)
注意:
- a.点击确认返回true,点击取消返回false
- b.confirm()方法弹出的对话框是模态对话框
- 在对话框关闭之前程序暂停,直到关闭后才继续执行
当点击“点我”按钮后:
当单击“确定”过后:
三、间隔调用和延迟调用
3.1 间隔调用 setInterval
-
描述:间隔调用全称为间隔调用函数,又名定时器。是一种能够每间隔一定时间自动执行一次的函数
-
语法:var timer = null;
timer = setInterval(需要执行的函数名或匿名函数,执行间隔时间ms);
<script>
var timer = null;
timer = setInterval(function() {
console.log('hello world');
}, 2000);
</script>
效果如下图:
3.2 清除间隔调用
- 语法:clearInterval(变量标识)
注意:
- 间隔调用的返回值是一个数字队列,因此通过访问数字队列来清除间隔调用也被允许。
<script>
setInterval(function() {
console.log('这是第1个定时器')
}, 2000);
setInterval(function() {
console.log('这是第2个定时器')
}, 2000);
setInterval(function() {
console.log('这是第3个定时器')
}, 2000);
clearInterval(2);
clearInterval(1);
</script>
效果图如下:
- 如果间隔调用的函数需要传入参数,则间隔调用需要使用如下的方式声明
- 语法:var timer = null;
timer = setInterval(字符串,执行间隔事件ms);
<script>
var timer = null;
function test(tmp) {
console.log(tmp);
}
timer = setInterval('test("csdn")', 1000);
</script>
效果图如下:
- 间隔调用不是立即执行,而是在【任务队列中的任务完成后】才执行间隔调用
- 因为间隔调用函数的实际执行者是window,因此间隔调用内部的this指向window
<script>
var timer = null;
timer = setInterval(function() {
console.log(this);
}, 2000);
</script>
效果图如下:
3.3 延迟调用 setTimeout
- 描述:延迟调用又叫延迟调用函数。是一种能够等待一定时间后在执行的函数
- 语法:var timer = null;
timer = setTimeout(需要执行的函数名称或者匿名函数,等待的时间);
var timer = null;
timer = setTimeout(function() {
console.log('hello world');
}, 2000);
console.log(timer);
// 或
function fn(){
console.log(1);
}
let timer = setTimeout(fn,1000);
console.log(timer);
效果图如下:
3.4 清除延迟调用 clearTimeout()
clearTimeout(计时器对象)
let count = 0;
let timer = null;
timer = clearTimeout(function(){
console.log(1);
count++;
if(count == 5){
clearTimeout(timer);
}
},1000);
3.5 案例
首先先利用html和css搭出架子:
* {
margin: 0;
padding: 0;
}
.box {
width: 190px;
height: 270px;
color: #fff;
text-align: center;
margin: 100px auto;
background-color: #d00;
padding-top: 40px;
box-sizing: border-box;
}
.box>h3 {
font-size: 26px;
}
.box>p:nth-of-type(1) {
color: rgba(255, 255, 255, .5);
margin-top: 5px;
}
.box>i {
display: inline-block;
margin-top: 5px;
margin-bottom: 5px;
font-size: 40px;
}
.box>.time {
display: flex;
justify-content: center;
margin-top: 10px;
}
.time>div {
width: 40px;
height: 40px;
background: #333;
line-height: 40px;
text-align: center;
font-weight: 700;
position: relative;
}
.time>div::before {
content: "";
display: block;
width: 100%;
height: 2px;
background: #d00;
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
}
.time>.minute {
margin: 0 10px;
}
<div class="box">
<h3>京东秒杀</h3>
<p>FLASH DEALS</p>
<i class="iconfont icon-lightningbshandian"></i>
<p>本场距离结束还剩</p>
<div class="time">
<div class="hour">00</div>
<div class="minute">00</div>
<div class="second">00</div>
</div>
</div>
再来设计其逻辑部分:
获取相关元素
定义一个处理两个时间差的函数,需要注意的是对于小时、分钟、秒钟如果小于10,那么应该在前面添加“0”来占位,最后利用对象的形式将其返回
为了实现其一个动态的效果,我们可以利用setInterval(),将获取到的时分秒全部放入进去,使其每隔一秒就变化一次
为了用户一打开就能看到效果,我们可以将获取到的时分秒封装到一个函数里,在setInterval()里和外直接调用函数即可实现
//1.获取需要操作的元素
const oHour = document.querySelector(".hour");
const oMinute = document.querySelector(".minute");
const oSecond = document.querySelector(".second");
//2.处理时间差
const remDate = new Date("2021-10-28 23:59:59");
setTime(remDate);
//开启定时器
setInterval(function() {
setTime(remDate);
}, 1000);
//为了让用户一进来就看得到效果,而不是先是三个00
// 我们可以对其进行封装处理
function setTime(remDate) {
const obj = getDifferTime(remDate);
// console.log(obj);
//3.将差值设置给元素
oHour.innerText = obj.hour;
oMinute.innerText = obj.minute;
oSecond.innerText = obj.second;
}
function getDifferTime(remDate, curDate = new Date()) {
//1.得到两个时间之间的差值(毫秒)
const differTime = remDate - curDate;
//2.得到两个时间之间的差值(秒 )
const differSecond = differTime / 1000;
//3.利用相差的总秒数 / 每一天的秒数 = 相差的天数
let day = Math.floor(differSecond / (60 * 60 * 24));
day = day >= 10 ? day : "0" + day;
//4.利用相差的总秒数 / 小时 % 24
let hour = Math.floor(differSecond / (60 * 60) % 24);
hour = hour >= 10 ? hour : "0" + hour;
//5.利用相差的总秒数 / 分钟 % 60
let minute = Math.floor(differSecond / 60 % 60);
minute = minute >= 10 ? minute : "0" + minute;
// 6.利用相差的总秒数 % 秒数
let second = Math.floor(differSecond % 60);
second = second >= 10 ? second : "0" + second;
return {
day: day,
hour: hour,
minute: minute,
second: second,
}
}
四、数据解析
- 概念:将[不能被直接使用的数据]通过某种方法转变成[能够被直接使用的数据]的过程称为数据解析
- 对于开发者来说最常见的数据解析就是将 [字符串数据 ] 解析为 [对象数据]
接下来我们看一个案例,以此来加深理解:
思路:
- 首先当点击提交按钮后需获取到相关的数据
- 将获取到的数据进行拆分
- 定义一个对象,接收拆分后的数据
- 最后,将此封装成一个函数,直接调用即可
<form action="" method="get">
姓名:<input type="text" name="userName" /><br/> 年龄:
<input type="text" name="age" /><br/> 性别:
<input type="text" name="sex" /><br/>
<input type="submit" />
</form>
<br/>
<button>解析数据</button>
<script>
// 获得元素
var but = document.querySelector('button');
// 封装成一个函数
function dataParse(outInfo) {
// 定义一个对象,用来接收切割后的数据
var obj = {};
var infoStr = outInfo;
//获取?后面的数据
var realInfo = infoStr.slice(1);
var proArr = realInfo.split('&');
for (var i = 0; i < proArr.length; i++) {
var tempArr = proArr[i].split('=');
// 接收数据
obj[tempArr[0]] = tempArr[1];
}
return obj;
}
// 获得数据
but.onclick = function() {
// 调用函数
var dataObj = dataParse(document.location.search);
console.log(dataObj);
}
</script>
效果图如下:
当点击“提交”按钮后:
当点击“数据解析”按钮后:
五、页面加载优化
- JavaScript引擎:由渲染引擎和javascript解释器构成
- javascript解释器:能够将js代码解释成可以在网页中实现的工具
渲染引擎:
- 1.解析代码:HTML代码解析为DOM树
- 2.对象的合成:CSS和DOM合成一颗渲染树
- 3.布局:计算出渲染树的布局
- 4.绘制:将渲染树绘制到屏幕
5.1 defer–推迟
- 描述:等待DOM加载完成后才去加载JS脚本避免因外部文件过大或者网络卡顿造成的文件阻塞
<!-- 模拟页面中的大文件或者网络卡顿 -->
<script src="js/01js.js" defer></script>
//01js.js
while(true){
console.log('hello world');
}
<script>
console.log('哈哈哈');
</script>
效果图如下:
5.2 async–异步
- 描述:DOM加载和js脚本加载异步执行,同时进行
- 优势:避免了因DOM文件过大导致的【文件加载阻塞】
- 弊端:不能确定谁先加载完成
<!-- 模拟页面中的大文件或者网络卡顿 -->
<script src="js/01js.js" async></script>
//01js.js
while(true){
console.log('hello world');
}
<script>
console.log('哈哈哈');
</script>
效果图如下:
六、回流和重绘
6.1 概述
- 回流(reflow):当页面中的部分或者全部元素改变宽度和高度、或者位置发生变化、删除或者增加某个或者某些元素时、某个元素隐藏或者显示时,这时页面的重新加载被称为是回流。
<div></div>
<span>123</span>
div {
width: 100px;
height: 100px;
background-color: orange;
}
<script>
document.querySelector('div').style.display = 'none';
</script>
效果图如下:
- 重绘(repaint):当页面的中的可见性发上变化时,我们说页面发生了重绘。比如:背景颜色吗,文字颜色等。
<div></div>
div {
width: 100px;
height: 100px;
background-color: orange;
}
<script>
document.querySelector('div').style.backgroundColor = 'red';
</script>
效果图如下:
- 通过上述概念我们能得到一个结论:回流必将引起重绘,而重绘不一定引起回流。
- 重绘的代价是高昂的,因为浏览器必须验证DOM树上其他节点元素的可见性
- 回流是浏览器性能的关键,因为其变化涉及到部分页面(或者整个页面)的布局。一个元素的回流导致了其所有子元素以及DOM中紧随其后的祖先元素的随后回流。
6.2 从重绘和回流方面提高浏览器性能
常见的引发重绘和回流的原因:
- 1.调整窗口大小
- 2.增加或者移除样式表
- 3.改变字体
- 4.内容变化,比如用户在input框中输入文字
- 5.激活CSS伪类,比如:hover
- 6.操作class属性
- 7.脚本操作DOM
- 8.计算offsetWidth和offsetHeight属性
- 9.设置style属性的值
如何从重绘和回流方面提高浏览器性能:
- 不要一项一项的去改变样式,尽可能一口气写完。(可以写在一起,不要被打断就行),最好使用.style或者.style.cssText
- 读写DOM也尽量也放在一起
- 使用文档碎片 var linshi = document.createDocumentFragment();
- 使用fixed或者absolute可以减少回流和重绘
- 使用
window.requestAnimationFrame(function (){
for(var i = 0;i < div.length; i++){
div[i].style.background=“red”;
}
})
把发生重绘的代码推迟到下一次发生重绘时一起执行
<div></div>
<span>123</span>
div {
width: 100px;
height: 100px;
background-color: orange;
}
<script>
var div = document.querySelector('div');
var span = document.querySelector('span');
div.onclick = function() {
//创建一个文档碎片,充当这些传概念元素的临时容器
var suipian = document.createDocumentFragment();
var h2_1 = document.createElement('h2');
h2_1.innerHTML = '第一个h2';
suipian.appendChild(h2_1);
var h2_2 = document.createElement('h2');
h2_2.innerHTML = '第二个h2';
suipian.appendChild(h2_1);
var h2_3 = document.createElement('h2');
h2_3.innerHTML = '第三个h2';
suipian.appendChild(h2_1);
var h2_4 = document.createElement('h2');
h2_4.innerHTML = '第四个h2';
suipian.appendChild(h2_1);
document.body.appendChild(suipian);
}
</script>
效果图如下:当单击div区域时: