BOM 浏览器对象模型
BOM 的概念
• BOM(Browser Object Model) 是指浏览器对象模型,浏览器对象模型提供了独立于内容的、可以与浏览器窗口进行互动的对象结构。BOM 由多个对象组成,其中代表浏览器窗口的 Window对象是 BOM 的顶层对象,其他对象都是该对象的子对象。
• 我们在浏览器中的一些操作都可以使用BOM的方式进行编程处理,比如:刷新浏览器、后退、前进、在浏览器中输入URL等。
window 对象
• window 是浏览器的顶级对象,当调用 window 下的属性和方法时,可以省略 window。
• 注意:window下两个特殊的属性 window.name、window.top。
对话框
对话框
alert()
prompt()
confirm()
加载事件
onload 事件
• 我们可以给 window 对象或者<img 等元素添加 onload 加载事件,表示只有绑定事件的元素加载完毕才能触发事件,才能执行事件函数。
• 其中 window 对象加载完毕:指的是所有HTML结构加载完,并且外部引入资源(js、css、img、视频)也加载完毕。
应用
• 利用 window.onload 事件,可以将 js 代码提前到 html 结构之前。
• 注意:一个页面中只能有一个 window.onload 事件
延时器
延时器
• 延时器是 window 对象的一个方法,类似于定时炸弹
• 语法:window.setTimeout(func,time);
• 第一个参数:延时执行的函数,可以是匿名函数定义,或者是一个函数名的引用,注意不要加 () 。
• 第二个参数:延时的时间,以毫秒计数,1000 毫秒等于 1 秒。
• 功能:在指定的时间后,延迟执行一个函数。
函数节流操作
清除延时器
• window 对象的一个方法
• 语法:window.clearTimeout(timeout);
• 参数:指定的延时器变量名引用。
• 功能:清除指定的延时器。
• 注意:清除的延时器需要存储到一个变量中,便于后期清除调用。
定时器
定时器
• 定时器是 window 对象的一个方法,相当于定时闹钟,每隔固定的时间响一次
• 语法:window.setInterval(func,interval);
• 第一个参数:每次执行的函数,可以是匿名函数定义,或者是一个函数名的引用,注意不要加 () 。
• 第二个参数:时间间隔,以毫秒计数,1000 毫秒等于 1 秒。
• 功能:每隔一个指定的时间,周期性的执行一个函数。
清除定时器
• window 对象的一个方法
• 语法:window.clearInterval(timer);
• 参数:指定的定时器变量名引用。
• 功能:清除指定的定时器。
• 注意:清除的定时器需要存储到一个变量中,便于后期清除调用。
简单运动
原理
• 简单运动:是一种视觉暂留效果,只要元素变化过程时间够短,给人眼造成的效果就是一个运动的效果,人眼的视觉残留的时间 0.1-0.4 秒之间。
• 制作方法:通过定时器,实现每隔一个极短的时间(50-100 毫秒左右),执行函数,函数内部让运动的属性值发生变化。
提高运动速度的方法
• 1、缩短时间间隔,增加了每秒移动的次数。
• 2、加大步长,让每一次走的步长增加。
清除定时器的问题
问题 1
• 将定时器的开始和停止过程书写在不同的事件函数内部,容易出现用户错误点击情况
1、多次点击开始,会造成加速
2、多次点击开始,不能够再停止
解决方法
• 设表先关
• 每次开启新定时器之前,都清除一次前面的定时器
问题 2
• 需求:要求元素走到指定位置停止,例如让元素停止在 500px 的位置.
• 问题:如果步长设置的不合理,停止的位置可能不是正好在 500 处
解决方法
• 拉终停表
• 在定时器内部每次都要判断是否走到了终点,要不要停止定时器
• 如果走到或超过了终点,强行拉到重点,并停止定时器
问题 3
• 需求:在规定时间内让元素走到规定的结束位置,时间间隔可以更改
• 例如:让元素在 2 秒钟内,left 属性从 0 走到 500px
解决方法
• 步标整除
• 总距离 = 步长 * 次数;
• 时间间隔自定义,总时长固定
• 求出总次数 = 总时间 / 时间间隔
• 定义计数器变量,每执行一次定时器函数增加计数 1,直到执行达到总次数,停止定时
封装动画函数
多属性运动
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
.box {
position: absolute;
top: 100px;
left: 0;
width: 100px;
height: 100px;
background-color: skyblue;
}
</style>
</head>
<body>
<input type="button" value="开始" id="start">
<div class="box" id="box"></div>
<script>
// 简单运动
// 获取元素
var start = document.getElementById("start");
var box = document.getElementById("box");
// 已知 开始位置、结束位置、总时长、时间间隔
// 总距离 = 步长 * 总次数
// 总距离 = 结束位置 - 起始位置 ,已知可以求出来
// 总次数 = 总时长 / 时间间隔 ,已知可以求出来
// 步长 = (结束位置 - 起始位置) / (总时长 / 时间间隔)
// 信号量 ,也相当于初始值
var nowLeft = box.offsetLeft;
var nowWidth = box.offsetWidth;
// console.log(nowLeft);
// 结束位置
var endLeft = 500;
var endWidth = 200;
// 总时长
var time = 1000;
// 时间间隔
var interval = 50;
// 运算总次数
var maxcount = time / interval;
// 运算出每一次的步长
var stepLeft = (endLeft - nowLeft) / maxcount;
var stepWidth = (endWidth - nowWidth) / maxcount;
// 定义一个次数的累加器
var count = 0;
// 准备条件结束可以开始定时器了
var timer;
start.onclick = function () {
timer = setInterval(function () {
// 让元素的属性每一次变化一个步长
nowLeft += stepLeft;
nowWidth += stepWidth;
// 每运动一次让次数累加器加 1
count++;
// 停止定时器
if (count >= maxcount) {
// 拉终停表
nowLeft = endLeft;
nowWidth = endWidth;
clearInterval(timer);
}
// 给属性赋值
box.style.left = nowLeft + "px";
box.style.width = nowWidth + "px";
},interval);
};
</script>
</body>
</html>
封装动画函数
• 单一动画
• 多属性动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
.box {
position: absolute;
top: 100px;
left: 0;
width: 100px;
height: 100px;
opacity: 0.5;
background-color: #0f0;
}
</style>
</head>
<body>
<input type="button" value="开始" id="start">
<div class="box" id="box"></div>
<script src="animate.js"></script>
<script>
// 获取元素
var start = document.getElementById("start");
var box = document.getElementById("box");
// 补充 获取计算后样式的方法
// console.dir(window.getComputedStyle(box));
// console.log(window.getComputedStyle(box).width);
// console.log(window.getComputedStyle(box).height);
// console.log(window.getComputedStyle(box).backgroundColor);
// console.log(window.getComputedStyle(box)["opacity"]);
// box 是需要运动的元素
// 多属性运动
// 共同点:总时间、时间间隔是相同的,自定义
// 不同的:起始位置(通过程序方法自动获取当前页面最终显示效果)和结束位置(自定义指定的)
// 函数参数
// 参数1:元素对象,它要进行运动
// 参数2:结束位置,以对象数据方式传递
// 参数3:总时间
// 调用函数
start.onclick = function () {
animate(box,{left: 400,width: 300},2000);
};
</script>
</body>
</html>
封装animate函数:
// 多属性运动
// 共同点:总时间、时间间隔是相同的,自定义
// 不同的:起始位置(通过程序方法自动获取当前页面最终显示效果)和结束位置(自定义指定的)
// 函数参数
// 参数1:元素对象,它要进行运动
// 参数2:结束位置,以对象数据方式传递
// 参数3:总时间
// 自定义动画函数
function animate(ele,end,time) {
// 已知结束位置,总时间,时间间隔 50,缺少起始位置
// 起始位置要根据结束位置提供的属性进行获取
// 起始位置的对象,需要先定义一个空对象
var now = {};
// start.left = 100;
// end 对象遍历,获取属性名
for (var k in end) {
now[k] = parseFloat(window.getComputedStyle(ele)[k]);
}
// console.log(start);
// 自定义时间间隔
var interval = 50;
// 计算总次数
var maxCount = time / interval;
// 次数累加器
var count = 0;
// 对象中的每个属性都有自己的步长,也可以放到一个步长对象中
var step = {};
// 遍历结束对象,计算每个属性的步长
for (var k in end) {
step[k] = (end[k] - now[k]) / maxCount;
}
// console.log(step);
// ====================准备工作结束,开启定时器=========================
var timer;
timer = setInterval(function () {
// 让每个属性发生变化,赋值给 now 对象中的每一项
for (var k in end) {
now[k] += step[k];
}
// 累计运动次数
count++;
// 判断定时器是否结束
if (count >= maxCount) {
// 拉终停表
for (var k in end) {
now[k] = end[k];
}
clearInterval(timer);
}
// 赋值给对应元素对象属性
for (var k in now) {
// 判断如果是 不透明度属性,不要加px单位
if (k === "opacity") {
ele.style[k] = now[k];
} else {
ele.style[k] = now[k] + "px";
}
}
},interval);
}
页面特效
案例
• 简单无缝滚动
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
.scroll {
position: relative;
width: 830px;
height: 130px;
border: 10px solid #000;
margin: 100px auto;
overflow: hidden;
}
.scroll ul {
position: absolute;
top: 0;
left: 0;
width: 5000px;
height: 130px;
}
.scroll ul li {
float: left;
width: 200px;
height: 130px;
margin-right: 10px;
overflow: hidden;
}
</style>
</head>
<body>
<div class="scroll" id="scroll">
<ul id="munit">
<li><img src="images/shuzi/0.png" alt="" /></li>
<li><img src="images/shuzi/1.png" alt="" /></li>
<li><img src="images/shuzi/3.png" alt="" /></li>
<li><img src="images/shuzi/4.png" alt="" /></li>
<li><img src="images/shuzi/5.png" alt="" /></li>
<li><img src="images/shuzi/6.png" alt="" /></li>
<li><img src="images/shuzi/7.png" alt="" /></li>
<li><img src="images/shuzi/0.png" alt="" /></li>
<li><img src="images/shuzi/1.png" alt="" /></li>
<li><img src="images/shuzi/3.png" alt="" /></li>
<li><img src="images/shuzi/4.png" alt="" /></li>
<li><img src="images/shuzi/5.png" alt="" /></li>
<li><img src="images/shuzi/6.png" alt="" /></li>
<li><img src="images/shuzi/7.png" alt="" /></li>
</ul>
</div>
<script src="common.js"></script>
<script>
// 1.获取元素
var scroll = my$("scroll");
var munit = my$("munit");
// 2.自己进行滚动播放
var nowLeft = 0;
// 关键点:找到 ul 元素的运动的折返点
// 耦合性比较强,只适用于一部分程序
var back = -1470;
var timer;
timer = setInterval(run, 10);
// 3.鼠标移上 scroll 元素,让运动停止
scroll.onmouseover = function() {
clearInterval(timer);
};
// 4.鼠标离开 scroll 元素,让运动重新开始
scroll.onmouseout = function() {
timer = setInterval(run, 10);
};
// 运动函数
function run() {
// nowLeft 进行自减
nowLeft -= 2;
// 每次都要判断,是否走到了折返点,如果走到了,让他瞬间切换到 0
if (nowLeft <= back) {
nowLeft = 0;
}
// 给ul 赋值
munit.style.left = nowLeft + "px";
}
</script>
</body>
</html>
• 高级无缝滚动
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
.scroll {
position: relative;
width: 830px;
height: 130px;
border: 10px solid #000;
margin: 100px auto;
overflow: hidden;
}
.scroll .inner {
position: relative;
width: 5000px;
}
.scroll ul {
position: absolute;
top: 0;
left: 0;
height: 130px;
list-style: none;
}
.scroll ul li {
float: left;
margin-right: 10px;
}
</style>
</head>
<body>
<div class="scroll" id="scroll">
<div class="inner">
<ul id="munit">
<li><img src="images/shuzi/0.png" alt="" /></li>
<li><img src="images/shuzi/1.png" alt="" /></li>
<li><img src="images/shuzi/2.png" alt="" /></li>
<li><img src="images/shuzi/3.png" alt="" /></li>
<li><img src="images/shuzi/4.png" alt="" /></li>
<li><img src="images/shuzi/5.png" alt="" /></li>
<li><img src="images/shuzi/6.png" alt="" /></li>
</ul>
</div>
</div>
<script src="common.js"></script>
<script>
// 获取元素
var scroll = my$("scroll");
var munit = my$("munit");
// 2.折返点计算需要通过 js 自动计算
var back = -munit.offsetWidth;
console.log(back);
// 1.自动生成另一组对应的图片结构 li
munit.innerHTML = munit.innerHTML + munit.innerHTML;
// 自己进行滚动播放
var nowLeft = 0;
var timer;
timer = setInterval(run, 5);
// 3.鼠标移上 scroll 元素,让运动停止
scroll.onmouseover = function() {
clearInterval(timer);
};
// 4.鼠标离开 scroll 元素,让运动重新开始
scroll.onmouseout = function() {
timer = setInterval(run, 5);
};
// 运动函数
function run() {
// nowLeft 进行自减
nowLeft -= 1;
// 每次都要判断,是否走到了折返点,如果走到了,让他瞬间切换到 0
if (nowLeft <= back) {
nowLeft = 0;
}
// 给ul 赋值
munit.style.left = nowLeft + "px";
}
</script>
</body>
</html>
• 点击切换的轮播图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
ul,
ol {
list-style: none;
}
.carousel {
position: relative;
width: 880px;
height: 550px;
border: 1px solid #333;
margin: 50px auto;
}
.pic li {
position: absolute;
left: 0;
top: 0;
width: 880px;
height: 550px;
display: none;
}
.pic li.current {
display: block;
}
.btn a {
position: absolute;
top: 50%;
width: 80px;
height: 80px;
margin-top: -40px;
background-color: rgba(255, 255, 255, 0.3);
text-decoration: none;
color: #444;
text-align: center;
line-height: 80px;
font-size: 60px;
font-family: "SimSun";
}
.btn .left {
left: 10px;
}
.btn .right {
right: 10px;
}
.btn a:hover {
background-color: rgba(255, 255, 255, 0.7);
}
.sub {
position: absolute;
bottom: 30px;
left: 50%;
width: 200px;
height: 40px;
margin-left: -100px;
border-radius: 20px;
background-color: rgba(255, 255, 255, 0.3);
}
.sub li {
float: left;
width: 20px;
height: 20px;
margin: 10px;
border-radius: 50%;
background-color: #ccc;
cursor: pointer;
}
.sub li.current {
background-color: #0ff;
}
</style>
</head>
<body>
<div class="carousel" id="carousel">
<ul class="pic" id="pic">
<li class="current"><img src="images/lunbo/01.jpg" alt=""></li>
<li><img src="images/lunbo/02.jpg" alt=""></li>
<li><img src="images/lunbo/03.jpg" alt=""></li>
<li><img src="images/lunbo/04.jpg" alt=""></li>
<li><img src="images/lunbo/05.jpg" alt=""></li>
</ul>
<div class="btn" id="btn">
<a href="javascript:;" class="left" id="leftbtn"><</a>
<a href="javascript:;" class="right" id="rightbtn">></a>
</div>
<ol class="sub" id="sub">
<li class="current"></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ol>
</div>
<script src="common.js"></script>
<script>
// 编程思路:信号量编程,通过一个全局变量的信号量,在不同的事件函数中进行信息传递,让多个事件进行协同作业
// 1.获取元素
var carousel = my$("carousel");
var ul = my$("pic");
var ulLis = ul.children;
var leftbtn = my$("leftbtn");
var rightbtn = my$("rightbtn");
var ol = my$("sub");
var olLis = ol.children;
// 全局信号量,存储的是要展示的图片所在 li 的下标
var idx = 0;
// 2.右按钮事件,切换到下一张
rightbtn.onclick = rightHandle;
// 3.左按钮事件,切换到上一张
leftbtn.onclick = function() {
// 信号量自减
idx--;
// 判断 idx 是否是超过最小的下标,如果是,就相当于从第一张要切换到最后一张
if (idx < 0) {
idx = ulLis.length - 1;
}
// 调用一个切换函数
change();
};
// 4.下标小圆点事件,点哪个小圆点,对应展示图片
for (var i = 0; i < olLis.length; i++) {
// 存储自己的下标
olLis[i].index = i;
// 给每个小圆点添加点击事件,然后获取对应下标,赋值给信号量
olLis[i].onclick = function() {
// 获取当前点击的元素的下标
// this.index
idx = this.index;
// 调用一个切换函数
change();
};
}
// 5.轮播图自动播放,执行类似右按钮的事件
var timer;
timer = setInterval(rightHandle, 3000);
// 6.鼠标移上轮播图,停止自动轮播
carousel.onmouseover = function() {
clearInterval(timer);
};
// 7.鼠标离开轮播图,重新开始自动轮播
carousel.onmouseout = function() {
timer = setInterval(rightHandle, 3000);
};
// 定义 右按钮事件函数
function rightHandle() {
// 信号量自加
idx++;
// 判断 idx 是否是超过最大的下标,如果是,就相当于从最后一张要切换到第一张
if (idx > ulLis.length - 1) {
idx = 0;
}
// 调用一个切换函数
change();
}
// 定义切换函数
function change() {
// 排他思想
// 对应控制
// 让所有 li 标签清除类名
for (var i = 0; i < ulLis.length; i++) {
ulLis[i].className = "";
olLis[i].className = "";
}
// 保留自己
ulLis[idx].className = "current";
olLis[idx].className = "current";
}
</script>
</body>
</html>
• 返回顶部
// 获取页面卷动的距离
// document.body.scrollTop
// document.documentElement.scrollTop
// 获取元素
var header = my$("top");
var backtop = my$("totop");
// 1.在页面往下卷动到一定距离后,让 header 高度变低,让 返回顶部按钮出现
// 添加页面滚动事件
window.onscroll = function () {
// 判断卷动走的距离,如果超过 10 px,就让 header 变低,让 返回顶部按钮出现
if (document.documentElement.scrollTop > 10) {
header.className = "header fixed";
backtop.style.display = "block";
} else {
header.className = "header";
backtop.style.display = "none";
}
// console.log(document.documentElement.scrollTop);
};
// 变量存储定时器
var timer;
// 2.点击返回顶部,让页面以动画的方式跳转到页面顶部
backtop.onclick = function () {
// 终点
var target = 0;
// 起始点
var current = document.documentElement.scrollTop;
// 步长
var step = 30;
timer = setInterval(function () {
// 获取当前卷动的值,每次递减一个步长
current -= step;
// 停止定时器
if (current <= target) {
current = target;
clearInterval(timer);
}
// 重新赋值
document.documentElement.scrollTop = current;
},10);
};
location 对象
location 对象
• location 对象是 window 对象下的一个属性,使用的时候可以省略 window 对象
• location 可以获取或者设置浏览器地址栏的 UR
location 对象的成员
• 使用 chrome 的控制台查看
• 查 MDN:https://developer.mozilla.org/zh-CN/
• 成员:
• assign()/reload()/replace()
• hash/host/hostname/search/href……
URL
• 统一资源定位符 (Uniform Resource Locator, URL)
• URL的组成:scheme://host:port/path?query#fragment
• 例如:http://www.lagou.com:80/a/b/index.html?name=zs&age=18#bottom
• scheme:通信协议,常用的http,ftp,maito等
• host:主机,服务器(计算机)域名系统 (DNS) 主机名或 IP 地址。
• port:端口号,整数,可选,省略时使用方案的默认端口,如http的默认端口为80。
• path:路径,由零或多个’/‘符号隔开的字符串,一般用来表示主机上的一个目录或文件地址。
• query:查询,可选,用于给动态网页传递参数,可有多个参数,用’&‘符号隔开,每个参数的名和值用’='符号隔开。例如:name=zs
• fragment:信息片断,字符串,锚点.
history 对象
history 对象
• history 对象是 window 对象下的一个属性,使用的时候可以省略 window 对象
• history 对象可以与浏览器历史记录进行交互,浏览器历史记录是对用户所访问的页面按时间顺序进行的记录和保存。
• back()
• forward()
• go()
创建元素的三种方式
document.write()
innerHTML
document.createElement()
性能问题
- innerHTML 方法由于会对字符串进行解析,需要避免在循环内多次使用。
- 可以借助字符串或数组的方式进行替换,再设置给 innerHTML
- document.createElement 每添加到页面一次,导致页面结构尺寸变化,会触发 DOM 回流,也会消耗一些性能
建议
- 简单的内容添加可以任意选择方法
- 添加多个元素,但是内容比较简单时,可以借助字符串或数组的方式进行替换,再设置给 innerHTML
- 添加的元素功能比较复杂时,使用 document.createElement
回调函数
回调函数就是传递一个参数化的函数,就是将函数作为一个参数传到另一个主函数里面,当那一个主函数执行完之后,再执行传进去的作为参数的函数。走这个过程的参数化的函数就叫做回调函数。
也就是被作为参数传递到另一个函数(主函数)的那个函数就叫做回调函数。
线程和阻塞
多线程:一个进程可以同时进行多个任务的并行处理。
单线程:在某一时刻内只能执行特定的一个任务。
阻塞:一个任务在执行时,后面的任务必须等待。
非阻塞:一个任务在执行时,后面的任务不需要等待。
JS 是单线程非阻塞的。
同步和异步
同步处理:一个同步任务的执行,需要等待前面的任务加载完成。
异步处理:一个异步任务的执行,它后面的其他任务不需要等待,而异步任务自己也会立即执行,执行结束后,会通过状态、通知和回调来通知整个线程。
JS 中的异步
JS 中实现异步任务的执行,需要一些特殊的结构:
setInterval
setTimeout
事件触发
事件循环机制
JS 的是单线程,因此在执行时只有一个主线程,每个任务必须顺序执行。
如果当前任务执行时间过长,会导致接下来的所有任务都处于阻塞状态,进而导致浏览器卡死等我们不希望看到的状况。
为了解决阻塞问题,出现了事件循环机制(Event Loop)。