BOM
什么是 BOM ?
BOM(Browser Object Model)即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是 window。
BOM 由一系列相关的对象构成,并且每个对象都提供了很多方法与属性。
BOM 缺乏标准,JavaScript 语法的标准化组织是 ECMA,DOM 的标准化组织是 W3C,BOM 最初是Netscape 浏览器标准的一部分。
BOM 的构成:
BOM 比 DOM 更大,它包含DOM
window 对象是浏览器的顶级对象,它具有双重角色。
-
它是 JS 访问浏览器窗口的一个接口。
-
它是一个全局对象。定义在全局作用域中的变量、函数都会变成 window 对象的属性和方法。
在调用的时候可以省略 window,前面学习的对话框都属于 window 对象方法,如 alert()、prompt() 等。
**注意:**window下的一个特殊属性 window.name
页面加载事件
window.onload = function(){}
或者
window.addEventListener(“load”,function(){}); 【推荐使用】
window.onload 是窗口 (页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS 文件等), 就调用的处理函数。
<body>
<script>
// window.onload = function() {
// var btn = document.querySelector('button');
// btn.addEventListener('click',function() {
// alert('点击我');
// })
// }
window.addEventListener('load',function() {
var btn = document.querySelector('button');
btn.addEventListener('click',function() {
alert('点击我');
})
})
window.addEventListener('load',function() {
alert(22);
})
document.addEventListener('DOMContentLoaded',function() {
alert(33);
})
// load 等页面内容全部加载完毕,包含页面dom元素 图片 flash css 等等
// DOMContentLoaded 是DOM 加载完毕,不包含 flash css 等就可以执行 加载速度比 load 更快一些
</script>
<button>点击</button>
</body>
注意:
-
有了 window.onload 就可以把 JS 代码写到页面元素的上方(任意地方都可以),因为 onload 是等页面内容全部加载完毕,再去执行处理函数。
-
window.onload 传统注册事件方式 只能写一次,如果有多个,会以最后一个 window.onload 为准。
-
如果使用 addEventListener 则没有限制
定时器之setTimeout
// 1. setTimeout
// 语法规范: window.setTimeout(调用函数, 延时时间);
// 1. 这个window在调用的时候可以省略
// 2. 这个延时时间单位是毫秒 但是可以省略,如果省略默认的是0
// 3. 这个调用函数可以直接写函数 还可以写 函数名 还有一个写法 '函数名()'
// 4. 页面中可能有很多的定时器,我们经常给定时器加标识符 (名字)
// setTimeout(function() {
// console.log('时间到了');
// }, 2000);
function callback() {
console.log('爆炸了');
}
var timer1 = setTimeout(callback, 3000);
var timer2 = setTimeout(callback, 5000);
// setTimeout('callback()', 3000); // 我们不提倡这个写法
5秒之后 自动关闭广告 案例
<body>
<img src="./images/ad.jpg" alt="" class="ad">
<script>
var ad = document.querySelector('.ad');
setTimeout(function() {
ad.style.display = 'none';
},5000)
</script>
</body>
清除定时器 setTimeout
<body>
<button>点击停止定时器</button>
<script>
var btn = document.querySelector('button');
var timer = setTimeout(function() {
console.log('爆炸了');
},5000);
btn.addEventListener('click',function() {
clearTimeout(timer);
})
</script>
</body>
倒计时效果(setInterval)
<body>
<div>
<span class="hour">00</span>
<span class="minute">00</span>
<span class="second">00</span>
</div>
<script>
// 1. 获取元素
var hour = document.querySelector('.hour');
var minute = document.querySelector('.minute');
var second = document.querySelector('.second');
var inputTime = +new Date('2021-11-11 20:00:00'); // 用户输入时间总的毫秒数
countDown(); // 我们先调用一次这个函数,防止第一次刷新页面有空白
// 2. 开启定时器
setInterval(countDown,1000);
function countDown() {
var nowTime = +new Date(); // 当前时间总的毫秒数
var times = (inputTime - nowTime) / 1000; // times是剩余时间总的秒数
var h = parseInt(times/60/60 %24); //计算小时
h = h < 10 ? '0' + h: h;
hour.innerHTML = h; // 把剩余的小时给 小时黑色的盒子
var m = parseInt(times/60%60); //计算分数
m = m < 10 ? '0' + m: m;
minute.innerHTML = m; // 把剩余的分钟给 分钟黑色的盒子
var s = parseInt(times%60); //计算当前秒数
s = s < 10 ? '0' + s: s;
second.innerHTML = s; // 把剩余的秒数给 秒数黑色的盒子
}
</script>
</body>
清除定时器 setInterval
<body>
<button class="begin">开启定时器</button>
<button class="stop">停止定时器</button>
<script>
var begin = document.querySelector('.begin');
var stop = document.querySelector('.stop');
var timer = null; // 全局变量 null是一个空对象
begin.addEventListener('click', function() {
timer = setInterval(function() {
console.log('ni hao ma');
}, 1000);
})
stop.addEventListener('click', function() {
clearInterval(timer);
})
</script>
</body>
发送短信案例
<body>
<div>
手机号码:<input type="number" class=""><button>发送</button>
</div>
<script>
var btn = document.querySelector('button');
var time = 3; // 定义剩下的秒数
btn.addEventListener('click',function() {
btn.disabled = true;
var timer = setInterval(function() {
if (time == 0) {
clearInterval(timer);
btn.disabled = false;
btn.innerHTML = '发送';
time = 3; // 这个3需要重新开始
} else {
btn.innerHTML = '还剩'+ time +'秒才可点击';
time--;
}
},1000);
})
</script>
</body>
this指向问题
this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,一般情况下this的最终指向的是那个调用它的对象
现阶段,我们先了解一下几个this指向
-
全局作用域或者普通函数中this指向全局对象window(注意定时器里面的this指向window)
-
方法调用中谁调用this指向谁
-
构造函数中this指向构造函数的实例
JS执行机制
JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。
这是因为 Javascript 这门脚本语言诞生的使命所致——JavaScript 是为处理页面中用户的交互,以及操作 DOM 而诞生的。比如我们对某个 DOM 元素进行添加和删除操作,不能同时进行。 应该先进行添加,之后再删除。
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是: 如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。
同步和异步
为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程。于是,JS 中出现了同步和异步。
同步
前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。比如做饭的同步做法:我们要烧水煮饭,等水开了(10分钟之后),再去切菜,炒菜。
异步
你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情。比如做饭的异步做法,我们在烧水的同时,利用这10分钟,去切菜,炒菜。
他们的本质区别: 这条流水线上各个流程的执行顺序不同。
// 第一个问题
// console.log(1);
// setTimeout(function() {
// console.log(3);
// }, 1000);
// console.log(2);
// 2. 第二个问题
// console.log(1);
// setTimeout(function() {
// console.log(3);
// }, 0);
// console.log(2);
// 3. 第三个问题
console.log(1);
document.onclick = function() {
console.log('click');
}
console.log(2);
setTimeout(function() {
console.log(3)
}, 3000)
location 对象
window 对象给我们提供了一个 location 属性用于获取或设置窗体的 URL,并且可以用于解析 URL 。 因为这个属性返回的是一个对象,所以我们将这个属性也称为 location 对象。
URL
统一资源定位符 (Uniform Resource Locator, URL) 是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
URL 的一般语法格式为:
protocol://host[:port]/path/[?query]#fragment
http://www.itcast.cn/index.html?name=andy&age=18#link
location 对象的属性:
重点记住:href和search
点击跳转新的页面:
<body>
<button>点击</button>
<script>
var btn = document.querySelector('button');
btn.addEventListener('click',function() {
console.log(location.href);
location.href = 'http://www.baidu.com';
})
</script>
</body>
5秒钟之后跳转页面:
var div = document.querySelector('div');
var timer = 5;
countDo();
setInterval(countDo,1000);
function countDo() {
if(timer == 0) {
location.href = 'http://www.baidu.com';
} else {
div.innerHTML = '您将在'+ timer +'秒钟之后跳转到首页';
timer--;
}
}
获取URL参数数据
页面1:
<form action="index.html">
用户名: <input type="text" name="uname">
<input type="submit" value="登录">
</form>
<!-- form 在登陆是默认是 get 提交 -->
页面2
<body>
<div></div>
<script>
console.log(location.search); // ?uname=andy
// 1. 先去掉? substr('起始的位置',截取几个字符)
var params = location.search.substr(1); // uname=andy
// 2. 利用=把字符分割为数组 split('=');
var arr = params.split('=');
console.log(arr); // ["uname","andy"]
var div = document.querySelector('div');
// 3. 把数据写入div 中
div.innerHTML = arr[1] + '欢迎您';
</script>
</body>
location 对象的方法:
<body>
<button>点击</button>
<script>
var btn = document.querySelector('button');
btn.addEventListener('click',function() {
// 记录浏览历史,所以可以实现后退功能
// location.assign('http://www.baidu.cn');
// 不记录浏览历史,所以不可以实现后退功能
// location.replace('http://www.baidu.cn');
// 重新加载页面,相当于刷新按钮或者 f5
location.reload(); //括号里如果加上 true 是强制刷新(ctrl+f5)的意思
})
</script>
</body>
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 = ""; //电脑
}
history 对象
window 对象给我们提供了一个 history 对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的 URL。
页面1
<body>
<a href="list.html">点击去往列表页</a>
<button>前进</button>
<script>
var btn = document.querySelector('button');
btn.addEventListener('click',function() {
history.forward();
})
</script>
</body>
页面2
<body>
<a href="1116.html">点击去往首页</a>
<button>后退</button>
<script>
var btn = document.querySelector('button');
btn.addEventListener('click',function() {
history.back();
})
</script>
</body>
history 对象一般在实际开发中比较少用,但是会在一些 OA 办公系统中见到。
PC端网页特效
offset
offset 翻译过来就是偏移量, 我们使用 offset 系列相关属性可以动态的得到该元素的位置(偏移)、大小等。
-
获得元素距离带有定位父元素的位置
-
获得元素自身的大小(宽度高度)
-
注意: 返回的数值都不带单位
<style>
* {
margin: 0;
padding: 0;
}
.father {
position: relative;
width: 200px;
height: 200px;
background-color: pink;
margin: 200px;
}
.son {
width: 100px;
height: 100px;
background-color: purple;
margin: 45px;
}
.w {
width: 100px;
height: 100px;
background-color: skyblue;
margin: 0 auto 200px;
padding: 10px;
border: 15px solid red;
}
</style>
<body>
<div class="father">
<div class="son"></div>
</div>
<div class="w"></div>
<script>
// offset
var father = document.querySelector('.father');
var son = document.querySelector('.son');
// 1. 可以得到元素的偏移 位置 返回的不带单位的数值
console.log(father.offsetTop);
console.log(father.offsetLeft);
// 它以带有定位的父亲为准 如果没有父亲或者父亲没有定位 则以 body 为准
console.log(son.offsetLeft);
var w = document.querySelector('.w');
// 2. 可以得到元素的大小 宽度和高度 是包含padding + border + width
console.log(w.offsetWidth);
console.log(w.offsetHeight);
// 3. 返回带有定位的父亲 否则返回的是body
console.log(son.offsetParent);
console.log(son.parentNode); // 返回父亲 是最近一级的定位 亲爸爸 不管父亲有没有定位
</script>
</body>
offset 与 style 区别
元素偏移量 offset 系列
<style>
.box{
width: 200px;
height: 200px;
background-color: pink;
}
</style>
<body>
<div class="box"></div>
<script>
var box = document.querySelector('.box');
box.addEventListener('mousemove',function(e) {
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
this.innerHTML = 'x坐标是' + x + ' y坐标是' + y;
})
</script>
</body>
拖动模态框
<style>
.login-header {
width: 100%;
text-align: center;
height: 30px;
font-size: 24px;
line-height: 30px;
}
ul,
li,
ol,
dl,
dt,
dd,
div,
p,
span,
h1,
h2,
h3,
h4,
h5,
h6,
a {
padding: 0px;
margin: 0px;
}
.login {
display: none;
width: 512px;
height: 280px;
position: fixed;
border: #ebebeb solid 1px;
left: 50%;
top: 50%;
background: #ffffff;
box-shadow: 0px 0px 20px #ddd;
z-index: 9999;
transform: translate(-50%, -50%);
}
.login-title {
width: 100%;
margin: 10px 0px 0px 0px;
text-align: center;
line-height: 40px;
height: 40px;
font-size: 18px;
position: relative;
cursor: move;
}
.login-input-content {
margin-top: 20px;
}
.login-button {
width: 50%;
margin: 30px auto 0px auto;
line-height: 40px;
font-size: 14px;
border: #ebebeb 1px solid;
text-align: center;
}
.login-bg {
display: none;
width: 100%;
height: 100%;
position: fixed;
top: 0px;
left: 0px;
background: rgba(0, 0, 0, .3);
}
a {
text-decoration: none;
color: #000000;
}
.login-button a {
display: block;
}
.login-input input.list-input {
float: left;
line-height: 35px;
height: 35px;
width: 350px;
border: #ebebeb 1px solid;
text-indent: 5px;
}
.login-input {
overflow: hidden;
margin: 0px 0px 20px 0px;
}
.login-input label {
float: left;
width: 90px;
padding-right: 10px;
text-align: right;
line-height: 35px;
height: 35px;
font-size: 14px;
}
.login-title span {
position: absolute;
font-size: 12px;
right: -20px;
top: -30px;
background: #ffffff;
border: #ebebeb solid 1px;
width: 40px;
height: 40px;
border-radius: 20px;
}
</style>
<body>
<div class="login-header"><a id="link" href="javascript:;">点击,弹出登录框</a></div>
<div id="login" class="login">
<div id="title" class="login-title">登录会员
<span><a id="closeBtn" href="javascript:void(0);" class="close-login">关闭</a></span>
</div>
<div class="login-input-content">
<div class="login-input">
<label>用户名:</label>
<input type="text" placeholder="请输入用户名" name="info[username]" id="username" class="list-input">
</div>
<div class="login-input">
<label>登录密码:</label>
<input type="password" placeholder="请输入登录密码" name="info[password]" id="password" class="list-input">
</div>
</div>
<div id="loginBtn" class="login-button"><a href="javascript:void(0);" id="login-button-submit">登录会员</a></div>
</div>
<!-- 遮盖层 -->
<div id="bg" class="login-bg"></div>
<script>
// 1. 获取元素
var login = document.querySelector('.login');
var mask = document.querySelector('.login-bg')
var link = document.querySelector('#link');
var title = document.querySelector('#title');
// 2. 点击弹出层这个链接 link 让mask 和login 显示出来
var closeBtn = document.querySelector('#closeBtn');
link.addEventListener('click',function() {
mask.style.display = 'block';
login.style.display = 'block';
})
// 3. 点击 closeBtn 就隐藏 mask 和 login
closeBtn.addEventListener('click',function() {
login.style.display = 'none';
mask.style.display = 'none';
})
// 4. 开始拖拽
// (1) 当鼠标按下,就获得鼠标在盒子内的坐标
title.addEventListener('mousedown',function(e) {
var x = e.pageX -login.offsetLeft;
var y = e.pageY -login.offsetTop;
// (2) 鼠标移动的时候,把鼠标在页面中的坐标,减去 鼠标在盒子内的坐标就是模态框的left和top值
document.addEventListener('mousemove',move)
function move(e) {
login.style.left = e.pageX - x + 'px';
login.style.top = e.pageY - y + 'px';
}
// (3) 鼠标弹起就让鼠标移动事件移除
document.addEventListener('mouseup',function() {
document.removeEventListener('mousemove',move);
})
})
</script>
</body>
显示效果如下:
元素可视区 client 系列
client 翻译过来就是客户端,我们使用 client 系列的相关属性来获取元素可视区的相关信息。通过 client 系列的相关属性可以动态的得到该元素的边框大小、元素大小等。
<style>
div {
width: 200px;
height: 200px;
background-color: pink;
border:10px solid red;
}
</style>
<body>
<div></div>
<script>
// client 宽度 和我们offsetWidth最大区别就是不包含边框
var div = document.querySelector('div');
console.log(div.clientWidth);
</script>
</body>
立即执行函数
// 1. 立即执行函数:不需要调用,立马能够自己执行的函数
function fn() {
console.log(1);
}
fn();
// 2. 写法 也可以传递参数进来
// (function() {})() 或者 (function() {}());
(function(a) {
console.log(a);
})(4); // 第二个小括号可以看作是调用函数
(function sum(a,b) {
console.log(a + b);
} (4,5));
flexible 分析
(function flexible(window, document) {
// 获取的html 的根元素
var docEl = document.documentElement
// dpr 物理像素比
var dpr = window.devicePixelRatio || 1
// adjust body font size 设置我们body 的字体大小
function setBodyFontSize() {
// 如果页面中有body 这个元素 就设置body的字体大小
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
} else {
// 如果页面中没有body 这个元素,则等着 我们页面主要的DOM元素加载完毕再去设置body
// 的字体大小
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();
// set 1rem = viewWidth / 10 设置我们html 元素的文字大小
function setRemUnit() {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit()
// reset rem unit on page resize 当我们页面尺寸大小发生变化的时候,要重新设置下rem 的大小
window.addEventListener('resize', setRemUnit)
// pageshow 是我们重新加载页面触发的事件
window.addEventListener('pageshow', function(e) {
// e.persisted 返回的是true 就是说如果这个页面是从缓存取过来的页面,也需要从新计算一下rem 的大小
if (e.persisted) {
setRemUnit()
}
})
// detect 0.5px supports 有些移动端的浏览器不支持0.5像素的写法
if (dpr >= 2) {
var fakeBody = document.createElement('body')
var testElement = document.createElement('div')
testElement.style.border = '.5px solid transparent'
fakeBody.appendChild(testElement)
docEl.appendChild(fakeBody)
if (testElement.offsetHeight === 1) {
docEl.classList.add('hairlines')
}
docEl.removeChild(fakeBody)
}
}(window, document))
元素滚动 scroll 系列
scroll 翻译过来就是滚动的,我们使用 scroll 系列的相关属性可以动态的得到该元素的大小、滚动距离等。
<style>
div {
width: 200px;
height: 200px;
background-color: pink;
border: 10px solid red;
padding: 10px;
overflow: auto;
}
</style>
<body>
<div>
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
</div>
<script>
// scroll 系列
var div = document.querySelector('div');
console.log(div.scrollHeight);
console.log(div.clientHeight);
// 滚动事件 当我们滚动条发送变化会触发的事件
div.addEventListener('scroll',function() {
console.log(div.scrollTop);
})
</script>
</body>
仿淘宝固定侧边栏
<style>
* {
margin: 0;
padding: 0;
}
.header {
height: 200px;
background-color: purple;
}
.banner {
height: 600px;
background-color: blue;
}
.main {
height: 800px;
background-color: aquamarine;
}
.slider-bar {
position: absolute;
left: 50%;
top: 300px;
margin-left: 600px;
width: 45px;
height: 130px;
background-color: pink;
}
.w {
width: 1200px;
margin: 10px auto;
}
span {
display: none;
position: absolute;
bottom: 0;
}
</style>
<body>
<div class="slider-bar">
<span class="goBack">返回顶部</span>
</div>
<div class="header w">头部区域</div>
<div class="banner w">banner区域</div>
<div class="main w">主体区域</div>
<script>
// 1. 获取元素
var sliderbar = document.querySelector('.slider-bar');
var banner = document.querySelector('.banner');
// banner.offsetTop 就是被卷去头部的大小 一定要写到滚动的外面
var bannerTop = banner.offsetTop;
// 当我们侧边栏固定定位之后应该变化的数值
var sliderbarTop = sliderbar.offsetTop - bannerTop;
// 获取main
var main = document.querySelector('.main');
var goBack = document.querySelector('.goBack');
var mainTop = main.offsetTop
// 2. 页面滚动事件 scroll
document.addEventListener('scroll',function() {
// console.log(11);
console.log(window.pageYOffset);
// 3. 当我们页面被卷去的头部大于等于了 172 此时 侧边栏就要改为固定定位
if(window.pageYOffset >= bannerTop) {
sliderbar.style.position = 'fixed';
sliderbar.style.top = sliderbarTop + 'px';
} else {
sliderbar.style.position = 'absolute';
sliderbar.style.top = '300px';
}
// 4. 当我们页面滚动到main盒子,就显示 goBack 模块
if(window.pageYOffset >= mainTop) {
goBack.style.display = 'block';
} else {
goBack.style.display = 'none';
}
})
</script>
</body>
页面被卷去的头部兼容性解决方案
需要注意的是,页面被卷去的头部,有兼容性问题,因此被卷去的头部通常有如下几种写法:
-
声明了 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
三大系列总结
mouseenter 和mouseover的区别:
mouseenter 鼠标事件
-
当鼠标移动到元素上时就会触发 mouseenter 事件
-
类似 mouseover,它们两者之间的差别是
mouseover 鼠标经过自身盒子会触发,经过子盒子还会触发。 mouseenter 只会经过自身盒子触发 -
之所以这样,就是因为mouseenter不会冒泡
-
跟mouseleave搭配 鼠标离开 mouseleave 同样不会冒泡
动画函数的封装
<style>
div {
position: absolute;
width: 200px;
height: 200px;
background-color: pink;
}
span {
position: absolute;
left: 0;
top: 300px;
display: block;
width: 100px;
height: 100px;
background-color: red;
}
</style>
<body>
<div>div</div>
<span>span</span>
<script>
// 简单动画函数封装 obj 目标对象 target 目标位置
function animate(obj,target) {
var timer = setInterval(function() {
if(obj.offsetLeft >= target) {
// 停止动画 本质是停止定时器
clearInterval(timer);
}
obj.style.left = obj.offsetLeft + 1 + 'px';
},30);
}
var div = document.querySelector('div');
// 调用函数
animate(div,300);
var span = document.querySelector('span');
animate(span,500);
</script>
</body>
动画函数给不同元素记录不同定时器
如果多个元素都使用这个动画函数,每次都要var 声明定时器(每次声明都会在内存中开辟新的空间,比较浪费资源)。我们可以给不同的元素使用不同的定时器(自己专门用自己的定时器)。
核心原理:利用 JS 是一门动态语言,可以很方便的给当前对象添加属性。
修改后代码如下:
// 简单动画函数封装 obj 目标对象 target 目标位置
// 给不同的元素指定了不同的定时器
function animate(obj,target) {
// 当我们不断地点击按钮,这个元素的速度会越来越快,因为开启了太多的定时器
// 解决方案就是 让我们元素只有一个定时器执行
// 先清除以前的定时器,只保留当前的一个定时器执行
clearInterval(obj.timer);
obj.timer = setInterval(function() {
if(obj.offsetLeft >= target) {
// 停止动画 本质是停止定时器
clearInterval(timer);
}
obj.style.left = obj.offsetLeft + 1 + 'px';
},30);
}
var div = document.querySelector('div');
var span = document.querySelector('span');
var btn = document.querySelector('button');
// 调用函数
animate(div,300);
btn.addEventListener('click',function() {
animate(span,500);
})
缓动效果原理
<body>
<button class="btn500">点击开始前进至500</button>
<button class="btn800">点击前进至800</button>
<span>span</span>
<script>
// 简单动画函数封装 obj 目标对象 target 目标位置
// 给不同的元素指定了不同的定时器
function animate(obj,target,callback) {
// 当我们不断地点击按钮,这个元素的速度会越来越快,因为开启了太多的定时器
// 解决方案就是 让我们元素只有一个定时器执行
// 先清除以前的定时器,只保留当前的一个定时器执行
clearInterval(obj.timer);
obj.timer = setInterval(function() {
// 步长值写到定时器里面
// var step = Math.ceil((target - obj.offsetLeft) / 10);
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if(obj.offsetLeft == target) {
// 停止动画 本质是停止定时器
clearInterval(obj.timer);
// 回调函数写到定时器结束里面
if(callback) {
// 调用函数
callback();
}
}
// 把每次加1 这个步长值改为一个慢慢变小的值 步长公式:(目标值 - 现在的位置)/ 10
obj.style.left = obj.offsetLeft + step + 'px';
},15);
}
var span = document.querySelector('span');
var btn500 = document.querySelector('.btn500');
var btn800 = document.querySelector('.btn800');
// 调用函数
btn500.addEventListener('click',function() {
animate(span,500);
})
btn800.addEventListener('click',function() {
animate(span,800,function() {
// alert('你好吗');
span.style.backgroundColor = '#008c8c';
});
})
// 匀速动画就是 盒子是当前的位置 + 固定的值 10
// 缓动动画就是 盒子当前的位置 + 变化的值
</script>
</body>
动画函数封装到单独JS文件里面
因为以后经常使用这个动画函数,可以单独封装到一个JS文件里面,使用的时候引用这个JS文件即可。
-
单独新建一个JS文件
-
HTML文件引入 JS 文件
function animate(obj,target,callback) {
// 当我们不断地点击按钮,这个元素的速度会越来越快,因为开启了太多的定时器
// 解决方案就是 让我们元素只有一个定时器执行
// 先清除以前的定时器,只保留当前的一个定时器执行
clearInterval(obj.timer);
obj.timer = setInterval(function() {
// 步长值写到定时器里面
// var step = Math.ceil((target - obj.offsetLeft) / 10);
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if(obj.offsetLeft == target) {
// 停止动画 本质是停止定时器
clearInterval(obj.timer);
// 回调函数写到定时器结束里面
if(callback) {
// 调用函数
callback();
}
}
// 把每次加1 这个步长值改为一个慢慢变小的值 步长公式:(目标值 - 现在的位置)/ 10
obj.style.left = obj.offsetLeft + step + 'px';
},15);
}
动画函数的使用
<style>
.sliderbar {
position: fixed;
right: 0;
bottom: 100px;
width: 40px;
height: 40px;
text-align: center;
line-height: 40px;
cursor: pointer;
color: #fff;
}
.con {
position: absolute;
left: 0;
top: 0;
width: 200px;
height: 40px;
background-color: purple;
z-index: -1;
}
</style>
<script src="animate.js"></script>
<body>
<div class="sliderbar">
<span>⬅</span>
<div class="con">问题反馈</div>
</div>
<script>
var sliderbar = document.querySelector('.sliderbar');
var con = document.querySelector('.con');
// 当我们鼠标经过 sliderbar 就会让 con这个盒子滑动到左侧
// 当我们鼠标离开 sliderbar 就会让 con这个盒子滑动到右侧
sliderbar.addEventListener('mouseenter',function() {
// annimate(obj,target,callback);
animate(con,-160,function() {
// 当我们动画执行完毕就把 ⬅ 改为 ➡
sliderbar.children[0].innerHTML = '➡';
});
})
sliderbar.addEventListener('mouseleave',function() {
animate(con,0,function() {
// 当我们动画执行完毕就把 ➡ 改为 ⬅
sliderbar.children[0].innerHTML = '⬅';
});
})
</script>
</body>
筋斗云案例
<style>
* {
margin: 0;
padding: 0
}
ul {
list-style: none;
}
body {
background-color: black;
}
.c-nav {
width: 900px;
height: 42px;
background: #fff url(./images/rss.png) no-repeat right center;
margin: 100px auto;
border-radius: 5px;
position: relative;
}
.c-nav ul {
position: absolute;
}
.c-nav li {
float: left;
width: 83px;
text-align: center;
line-height: 42px;
}
.c-nav li a {
color: #333;
text-decoration: none;
display: inline-block;
height: 42px;
}
.c-nav li a:hover {
color: white;
}
.cloud {
position: absolute;
left: 0;
top: 0;
width: 83px;
height: 42px;
background: url(./images/cloud.gif) no-repeat;
}
</style>
<script src="animate.js"></script>
<script>
window.addEventListener('load',function() {
// 1. 获取元素
var cloud = document.querySelector('.cloud');
var c_nav = document.querySelector('.c-nav');
var lis = c_nav.querySelectorAll('li');
// 2. 给所有的小li绑定事件
// 这个current 作为筋斗云的起始位置
var current = 0;
for(var i = 0; i < lis.length; i++) {
lis[i].addEventListener('mouseenter',function() {
animate(cloud,this.offsetLeft);
});
lis[i].addEventListener('mouseleave',function() {
animate(cloud,current);
});
// 当我们鼠标点击,就把当前位置作为目标值
lis[i].addEventListener('click',function() {
current = this.offsetLeft;
});
}
})
</script>
<body>
<div id="c-nav" class="c-nav">
<span class="cloud"></span>
<ul>
<li class="current"><a href="#">首页新闻</a></li>
<li><a href="#">首页新闻</a></li>
<li><a href="#">首页新闻</a></li>
<li><a href="#">首页新闻</a></li>
<li><a href="#">首页新闻</a></li>
<li><a href="#">首页新闻</a></li>
<li><a href="#">首页新闻</a></li>
<li><a href="#">首页新闻</a></li>
</ul>
</div>
</body>