JS-API-BOM

BOM

1. BOM概述

  • BOM (Browser Object Model)浏览器对象模型

  • 它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是 window

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

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

DOM BOM
文档对象模型 浏览器对象模型
DOM 就是把文档当作一个对象来看待 把 浏览器当作一个对象来看待
DOM 的顶级对象是 document BOM 的顶级对象是 window
DOM 主要学习的是操作页面元素 BOM 学习的是浏览器窗口交互的一些对象(例如:刷新页面,跳转页面,页面滚动)
DOM 是 W3C 标准规范 BOM 是浏览器厂商在各自浏览器上定义的,兼容性较差

  • 它是 JS 访问浏览器窗口的一个接口

  • 它是一个全局对象。定义在全局作用域中的变量、函数都会变成 window 对象的属性和方法

  • 在调用的时候可以省略 window,前面学习的对话框都属于 window 对象方法,如 alert()、prompt()等。

  • 注意:window下的一个特殊属性 window.name

// 定义在全局作用域中的变量会变成window对象的属性
var num = 10;
console.log(window.num);
// 10
// 定义在全局作用域中的函数会变成window对象的方法
function fn() {
console.log(11);
}
console.fn();
// 11
var name = 10; //不要用这个name变量,window下有一个特殊属性window.name
console.log(window.num);

2. window 对象的常见事件

2.1 窗口加载事件

2.1.1window.onload

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

<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')
});
});
</script>
<button>点击</button>

注:

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

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

  • 如果使用addEventListener 则没有限制

2.1.2 DOMContentLoaded

document.addEventListener('DOMContentLoaded',function(){})

  • DOMCountentLoaded事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等,加载速度比load快。

  • 如果页面的图片很多的话, 从用户访问到onload触发可能需要较长的时间,
    交互效果就不能实现,必然影响用户的体验,
    此时用 DOMContentLoaded事件比较合适。

window.addEventListener("load", function () {
alert('22')//后弹出22,等所有内容加载完成才执行
});
window.addEventListener("DOMContentLoaded", function () {
alert('33')//先弹出33,bom元素加载完就可以执行
});

2.2 调整窗口的大小事件

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

window.addEventListener('resize', function () {
console.log('变化');
})
  • 我们经常利用这个事件完成响应式布局。window.innerWidth 当前屏幕的宽度
//例:当页面小于800px时div隐藏
<script>
window.addEventListener('load', function () {
var div = document.querySelector('div');
window.addEventListener('resize', function () {
if (window.innerWidth <= 800) {
div.style.display = 'none';
} else {
div.style.display = '';
}
})
})
</script>
<div></div>

3. 定时器

3.1 两种定时器

window 对象给我们提供了两个定时器:

  • setTimeout()

  • setInterval()

3.1.1 setTimeout()

window.setTimeout(调用函数,[延迟的毫秒数]);
经过毫秒数后调用函数,仅触发一次

setTimeout(function () {
console.log('时间到');
}, 2000)
function callback() {
console.log('时间到!');
}
setTimeout(callback, 3000);
//页面中可能有很多的定时器,我们经常给定时器加标识符 (名字)
var time1 = setTimeout(callback, 3000);
var time2 = setTimeout(callback, 3000);
  • 定时器调用的函数称为回调函数

  • 停止setTimeout()定时器

window.clearTimeout(timeoutID)

<button>stop!</button>
<script>
function callback() {
console.log('时间到!');
}
var time2 = setTimeout(callback, 3000);
//点击按钮停止定时器
var btn = document.querySelector('button');
btn.addEventListener('click', function () {
clearTimeout(time2);
})
</script>

3.1.2 setInterval()

window.setInterval(调用函数,[间隔毫秒数]);
间隔一次毫秒数后 就调用回调函数,触发多次

setInterval(function () {
console.log('叮');
}, 1000)

例:京东倒计时

点击查看代码
<style>
* {
margin: 0;
padding: 0;
}
.time {
width: 170px;
margin: 100px auto;
}
p {
float: left;
font-size: 24px;
}
.time div {
height: 35px;
width: 35px;
background-color: black;
margin: 0 3px;
float: left;
color: aliceblue;
font-size: 24px;
font-weight: 700;
text-align: center;
line-height: 35px;
}
</style>
</head>
<body>
<div class="time">
<div class="hour"></div>
<p>:</p>
<div class="min"></div>
<p>:</p>
<div class="sec"></div>
</div>
<script>
var hour = document.querySelector('.hour');
var min = document.querySelector('.min');
var sec = document.querySelector('.sec');
var inputtime = +new Date('2022-7-30 22: 00: 00');//返回用户输入时间总的毫秒数
countDown()//先调用一次函数,防止页面刷新时有空白
setInterval(countDown, 1000)
function countDown() {
var nowTime = +new Date();//返回当前时间总的号秒数
var del = inputtime - nowTime;
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;
min.innerHTML = m;
var s = parseInt(times % 60); //秒
s = s < 10 ? '0' + s : s;
sec.innerHTML = s;
// return d + '天' + h + '时' + m + '分' + s + '秒';
}
</script>
  • 停止senInterval()定时器

window.clearInterval(intervalID);

<button class="stop">停!</button>
<button class="begin">行!</button>```
var btn01 = document.querySelector('.stop');
var btn02 = document.querySelector('.begin');
//需要先将定时器设置为全局变量
var timer = null;
btn01.addEventListener('click', function () {
clearTimeout(timer);
})
btn02.addEventListener('click', function () {
timer = setInterval(function () {
console.log('ding');
}, 1000);
})

例:短信验证倒计时

<body>
<input type="text">
<button>发送短信</button>
</body>
<script>
var btn = document.querySelector('button');
var time = 9;
btn.addEventListener('click', function () {
btn.disabled = 'true';
var timer = setInterval(function () {
if (time == 0) {
clearInterval(timer);
btn.innerHTML = '发送短信';
btn.disabled = '';
time = 9;
} else {
btn.innerHTML = time + '秒后可再次发送短信';
time--;
}
}, 1000)
})

3.2 this指向问题

1. 全局作用域或者普通函数中this指向全局对象window
(注意定时器里面的this指向window(不论定时器的位置在哪,都是指向window))
console.log(this);
function fn(){
console.log(this)
}
window.fn();//fn的调用其实是省略了window.所以这里的this也是指向window的
2. 方法调用中谁调用this指向谁
var o = {
sayHi:function(){
console.log(this);// this指向的是o这个对象
}
}
o.sayHi();
var btn = document.querySelector('button');
btn.onclick = function(){
console.log(this); // this指向的是btn这个按钮对象
}
3. 构造函数中的this指向构造函数的实例
function Fun(){
console.log(this); // this 指向的是fun 实例对象
}
var fun = new Fun();

4. JS执行机制

4.1 JS是单线程

  • JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。这是因为 Javascript 这门脚本语言诞生的使命所致——JavaScript 是为处理页面中用户的交互,以及操作 DOM 而诞生的。比如我们对某个 DOM 元素进行添加和删除操作,不能同时进行。 应该先进行添加,之后再删除。

  • 单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是: 如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。

4.2 同步和异步

为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程。于是,JS 中出现了同步和异步。

  • 同步:前一个任务结束后再执行后一个任务

  • 异步:在做这件事的同时,你还可以去处理其他事情

js分为同步任务和异步任务:

  • 同步任务:同步任务都在主线程上执行,形成一个 执行栈

  • 异步任务:JS中的异步是通过回调函数实现的

    • 异步任务有以下三种类型:

      • 普通事件,如click,resize等;
      • 资源加载,如load,error等;
      • 定时器,包括setInterval,setTimeout等。
    • 异步任务相关回调函数添加到任务队列(消息队列)中

JS执行机制:

  • 先执行执行栈中的同步任务

  • 异步任务(回调函数)放入任务队列中

  • 一旦执行栈中的所有同步任务执行完毕,
    系统就会按次序读取任务队列中的异步任务,
    于是被读取的异步任务结束等待状态,进入执行栈,开始执行

异步进程处理:
同步任务放在执行栈中执行,
异步任务由异步进程处理放到任务队列中,
执行栈中的任务执行完毕会去任务队列中查看是否有异步任务执行,
由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环( event loop)。

5.location对象

window对象 提供的一个 location属性 用于获取或设置窗体的URL,并且可以用于解析URL。返回的是一个对象

5.1 URL

统一资源定位符(uniform resouce locator)是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。

url 的一般语法格式为:

protocol://host[:port]/path/[?query]#fragment
例:http://www.itcast.cn/index.html?name=andy&age=18#link
组成 说明
protocol 信协议 常用的http,ftp,maito等
host 主机(域名) www.itheima.com
port 端口号,可选
path 路径 由零或多个’/'符号隔开的字符串
query 参数 以键值对的形式,通过&符号分隔开来
fragment 片段 #后面内容 常见于链接 锚点

5.2 location 对象的属性

location对象属性 返回值
location.href 获取或者设置整个URL
location.host 返回主机(域名)www.baidu.com
location.port 返回端口号,如果未写返回空字符串
location.pathname 返回路径
location.search 返回参数
location.hash 返回片段 #后面内容常见于链接 锚点

5.2.1 location.href

例:倒计时跳转页面

<div></div>
<script>
var div = document.querySelector('div');
var timer = 5;
function fn() {
div.innerHTML = '页面将在' + timer + '秒钟后跳转';
timer--;
if (timer == 0) {
location.href = 'https://www.cnblogs.com/mtdj/'
}
}
fn();
setInterval(fn, 1000);
</script>

5.2.2 location.search

例:不同页面获取url参数

//login.html
<form action="index.html">
用户名:<input type="text" name="uname">
<input type="submit" value="登录">
</form>
//index.html
<div></div>
<script>
var div = document.querySelector('div');
console.log(location.search);//得到 ?uname=zhang
//使用substr('起始的位置','截取几个字符')来截取字符串
//利用split()来分隔等号两边
var prace = location.search.substr(1);
var names = prace.split('=');
div.innerHTML = names[1] + ',欢迎你!';
</script>

5.3 location 对象的方法

location对象方法 返回值
location.assign() 跟href一样,可以跳转页面(也称为重定向页面),记录历史,可以回退页面
location.replace() 替换当前页面,因为不记录历史,所以不能后退页面
location.reload() 重新加载页面,相当于刷新按钮或者 f5 ,如果参数为true 强制刷新 ctrl+f5

6. navigator对象

  • navigator 对象包含有关浏览器的信息,它有很多属性

  • 我们常用的是userAgent,该属性可以返回由客户机发送服务器的user-agent头部的值

  • 下面前端代码可以判断用户是用哪个终端打开页面的,如果是用 PC 打开的,我们就跳转到 PC 端的页面,如果是用手机打开的,就跳转到手机端页面

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 = ""; //电脑
}

7. history 对象

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

history对象方法 作用
back() 可以后退功能
forward() 前进功能
go(参数) 前进后退功能,参数如果是 1 前进1个页面 如果是 -1 后退1个页面
<body>
<a href="list.html">点击我去往列表页</a>
<button>前进</button>
<script>
var btn = document.querySelector('button');
btn.addEventListener('click', function() {
// history.forward();
history.go(1);
})
</script>
</body>

PC端网页特效

1. 元素偏移量offset系列

1.1 offset概述

使用offset相关属性可以动态的得到该元素的位置(偏移)、大小等。

  • 获得元素距离带有定位父元素的位置

  • 获得元素自身的大小(宽、高)

  • 注:返回的数值不带单位

offset常用属性 作用
element.offsetParent 返回该元素带有定位的父元素,如果父级都没有定位返回body
element.offsetTop 返回元素带有定位父元素距离上方的偏移量
element.offsetLeft 返回元素带有定位父元素距离左边的偏移量
element.offsetWidth 返回自身的宽度,包括padding、border、内容区,返回值不带单位px
element.offsetHeight 返回自身的高度,包括padding、border、内容区,返回值不带单位px
<div class="father">
<div class="son"></div>
</div>
<script>
var father = document.querySelector('.father');
var son = document.querySelector('.son');
//他以带有定位的父元素为准,如果没有父元素或是父元素没有定位,则以body为准
console.log(son.offsetTop);

1.2 offset和style的区别

例:点击位置在盒子中的位置

<div></div>
<script>
var div = document.querySelector('div');
div.addEventListener('click', function (e) {
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
console.log('( ' + x + ' , ' + y + ' )');
})

例:模态框的拖拽

<style>
* {
margin: 0;
padding: 0;
}
a {
text-decoration: none;
color: black;
}
.login_header {
width: 100%;
text-align: center;
font-size: 20px;
}
/* 登录框 */
.login {
display: none;
width: 450px;
height: 280;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
border: 1px solid gainsboro;
background-color: white;
box-shadow: 0px 0px 20px gainsboro;
/* 层级是要最大 */
z-index: 999;
}
.login_close {
text-align: center;
line-height: 25px;
width: 25px;
height: 25px;
border: 1px solid gainsboro;
position: absolute;
top: 0;
right: 0;
}
.login_input,
.login_title {
margin: 0 0 20px 0;
height: 35px;
text-align: center;
}
.login_title {
font-size: 18px;
line-height: 35px;
cursor: move;
}
.login_input {
width: 400px;
height: 35px;
line-height: 35px;
margin: 0 auto;
margin-bottom: 20px;
}
.login_input label {
width: 70px;
float: left;
text-align: right;
}
.login_input input {
height: 35px;
width: 300px;
border: 1px solid gainsboro;
}
.login_sub {
width: 180px;
margin: 30px auto;
}
.login_sub input {
height: 35px;
width: 180px;
background-color: white;
border: 1px solid gainsboro;
}
/* 遮罩层 */
.login_bg {
display: none;
width: 100%;
height: 100%;
position: fixed;
top: 0px;
left: 0px;
background: rgba(0, 0, 0, 0.3);
}
</style>
</head>
<body>
<!-- 头部文字 -->
<div class="login_header"><a id='link' href="javascript:;">点击,弹出登录框</a></div>
<!-- 登录框 -->
<div class="login">
<div class="login_title">登录会员</div>
<div class="login_close">
<a id='closebtn' href="javascript:;">×</a>
</div>
<div class="login_input">
<label>用户名:</label>
<input type="text" placeholder="请输入用户名">
</div>
<div class="login_input">
<label>密码:</label>
<input type="password" placeholder="请输入密码">
</div>
<div class="login_sub">
<input type="submit" value="登录">
</div>
</div>
<!-- 遮罩层 -->
<div class="login_bg"></div>
<script>
var linkbtn = document.querySelector('#link');
var login = document.querySelector('.login');
var closebtn = document.querySelector('#closebtn');
var bg = document.querySelector('.login_bg');
var title = document.querySelector('.login_title');
linkbtn.addEventListener('click', function () {
login.style.display = 'block';
bg.style.display = 'block';
})
closebtn.addEventListener('click', function () {
login.style.display = '';
bg.style.display = '';
})
//拖拽事件分为三步:鼠标按下(mousedown)鼠标移动(mousemove)鼠标弹起(mouseup)
//移动事件 一定要写在 按下事件中
title.addEventListener('mousedown', function (e) {
//鼠标按下时在盒子里的坐标
var x = e.pageX - login.offsetLeft;
var y = e.pageY - login.offsetTop;
document.addEventListener('mousemove', fn)
function fn(e) {
//鼠标现在的坐标-鼠标在盒子内的坐标=盒子的left和top
login.style.left = e.pageX - x + 'px';
login.style.top = e.pageY - y + 'px';
}
//删除事件
document.addEventListener('mouseup', function () {
document.removeEventListener('mousemove', fn)
})
})
</script>

例:京东放大镜

<style>
.father {
width: 300px;
margin: 300px 0;
border: 1px solid gainsboro;
position: relative;
}
.son {
display: none;
width: 100px;
height: 100px;
position: absolute;
top: 0;
left: 0;
background-color: rgba(253, 223, 183, .5);
cursor: move;
}
.big {
display: none;
width: 400px;
height: 400px;
border: 1px solid gainsboro;
position: absolute;
top: 0;
left: 310px;
overflow: hidden;
}
.big img {
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body>
<div class="father"><img src="imges/壁纸4.webp" alt="" width="300px"">
<div class=" son"></div>
<div class="big"><img id="bimg" src="imges/壁纸4.webp" alt="" width="1200px"></div>
</div>
<script>
var father = document.querySelector('.father');
var son = document.querySelector('.son');
var big = document.querySelector('.big');
var bimg = document.querySelector('#bimg');
father.addEventListener('mouseover', function () {
son.style.display = 'block';
big.style.display = 'block';
})
father.addEventListener('mouseout', function () {
son.style.display = '';
big.style.display = '';
})
father.addEventListener('mousemove', fn)
function fn(e) {
var x = e.pageX - father.offsetLeft;
var y = e.pageY - father.offsetTop;
var x1 = x - son.offsetWidth / 2;
var y1 = y - son.offsetHeight / 2;
//x的最大移动距离=父盒子的长度-遮挡层的长度,宽度同理
var x_max = father.offsetWidth - son.offsetWidth;
var y_max = father.offsetHeight - son.offsetHeight;
//设置遮挡层移动范围
if (x1 <= 0) {
x1 = 0;
} if (x1 >= (x_max)) {
x1 = x_max;
}
if (y1 <= 0) {
y1 = 0;
} if (y1 >= y_max) {
y1 = y_max;
}
son.style.left = x1 + 'px';
son.style.top = y1 + 'px';
//xb的最大移动距离=大图片的长度-big的长度,宽度同理
var bx_max = bimg.offsetWidth - big.offsetWidth;
var by_max = bimg.offsetHeight - big.offsetHeight;
// 大图片的移动距离=遮挡层的移动距离*xb最大移动距离/x最大移动距离
var bx = x1 * (bx_max) / (x_max);
var by = y1 * (by_max) / (y_max)
if (bx <= 0) {
bx = 0;
} if (bx >= bx_max) {
bx = bx_max;
}
if (by <= 0) {
by = 0;
} if (by >= by_max) {
by = by_max;
}
bimg.style.left = -bx + 'px';
bimg.style.top = -by + 'px';
}
</script>

2.元素可视区client系列

client系列属性 作用
element.clientTop 返回元素上边框的大小
element.clientLeft 返回元素左边框的大小
element.clientWidth 返回自身包括padding、内容区的宽度,不含边框,返回值不带单位
element.clientHight 返回自身包括padding、内容区的高度,不含边框,返回值不带单位

2.1 淘宝flexible.js源码分析

2.1.1 立即执行函数

不需要调用,立马就能够自己执行

  • ( function(){} ) () //最后一个括号可以看做是调用函数,可存放实参
  • ( function(){} () )

注:多个立即执行函数中间要添加 分号 隔开
主要作用:创建了一个独立的作用域,避免命名冲突问题

2.1.2 物理像素比

window.devicePixelRatio

3. 元素滚动scroll系列

scroll系列属性 作用
element.scrollTop 返回(滚动条下滑时)被卷去的上侧距离,返回值不带单位
element.scrollLeft 返回(滚动条右滑时)被卷去的左侧距离,返回值不带单位
element.scrollWidth 返回自身实际的宽度,包含padding,不含边框,返回值不带单位
element.scrollTop 返回自身实际的高度,包含padding,不含边框,返回值不带单位

3.1 滚动事件 onscroll

滚动条发生变化时就会触发事件

例:固定侧边栏

<div class="slider_bar"><span>返回顶部</span></div>
<div class="header">头部区域</div>
<div class="banner">banner区域</div>
<div class="main">主体部分</div>
<script>
var bar = document.querySelector('.slider_bar');
var banner = document.querySelector('.banner');
var main = document.querySelector('.main');
var back = document.querySelector('span');
//banner 距离页面上边框的距离
var bannertop = banner.offsetTop;
//侧边栏 距离页面上边框的距离
var bartop = bar.offsetTop;
//主体部分 距离页面上边框的距离
var maintop = main.offsetTop;
document.addEventListener('scroll', function () {
//获取页面上卷距离用 window.pageYoffset
var x = window.pageYOffset;
//当页面拖到 banner时,侧边栏不动
if (x >= bannertop) {
bar.style.position = 'fixed';
bar.style.top = bartop - bannertop + 'px';
} else {
bar.style.position = '';
bar.style.top = '';
}
//当页面拖到 主体部分时,显示返回顶部
if (x >= maintop) {
back.style.display = 'block';
} else {
back.style.display = '';
}
})
</script>


4. mouseover 和mouseenter 的区别

  • mouseover 鼠标经过自身会触发,经过子元素时也会触发
    (鼠标经过子元素会冒泡,经过父元素 就再执行一次事件)

  • mouseenter 只有鼠标经过自身时才会触发(不会冒泡)

  • 和 mouseenter 搭配的鼠标离开时间 mouseleave 同样不会冒泡

5. 动画函数封装

4.1 动画实现原理

核心原理:通过定时器setIterval() 不断移动盒子位置。

4.2 简单动画函数的封装

<div></div>
<script>
var div = document.querySelector('div');
an(div, 400);
//简单动画函数封装obj 目标对象 target 目标位置
function an(obj, target) {
var timer = setInterval(function () {
if (obj.offsetLeft >= target) {
clearInterval(timer);
}
obj.style.left = obj.offsetLeft + 2 + 'px';
}, 50)
}
</script>

4.2.1 给不同对象添加不同的定时器

<div></div>
<script>
var div = document.querySelector('div');
an(div, 400);
//简单动画函数封装obj 目标对象 target 目标位置
function an(obj, target) {
//使用obj.方法名 可以给不同元素添加不同的定时器,div.timer / span.timer
obj.timer = setInterval(function () {
if (obj.offsetLeft >= target) {
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + 2 + 'px';
}, 50)
}

小问题:如果给事件添加一个点击按钮触发的条件

//当我们不断点击按钮时,这个元素的速度会越来越快,因为开启了太多的定时器
var btn = document.querySelector('button');
btn.addEventListener('click', function () {
an(div, 400);
})

解决方法:在开始执行定时器之前,清除掉之前的定时器

<script>
var div = document.querySelector('div');
//简单动画函数封装obj 目标对象 target 目标位置
function an(obj, target) {
clearInterval(obj.timer);//清除定时器
obj.timer = setInterval(function () {
if (obj.offsetLeft >= target) {
clearInterval(timer);
}
obj.style.left = obj.offsetLeft + 2 + 'px';
}, 50)
}
//当我们不断点击按钮时,这个元素的速度会越来越快,因为开启了太多的定时器
var btn = document.querySelector('button');
btn.addEventListener('click', function () {
an(div, 400);
})
</script>

4.3 缓动动画原理


每次走的距离都在减小

<script>
var div = document.querySelector('div');
//简单动画函数封装obj 目标对象 target 目标位置
function an(obj, target) {
clearInterval(obj.timer);
obj.timer = setInterval(function () {
if (obj.offsetLeft == target) {
clearInterval(obj.timer);
}
//x为这次应该走的距离=(目标值-现在的位置)/10
//这里要将步长值取整数(向上取整)
//考虑到向后退,因此当步长为负时要想下取整
var x = (target - obj.offsetLeft) / 10;
x = x > 0 ? Math.ceil(x) : Math.floor(x);
obj.style.left = obj.offsetLeft + x + 'px';
}, 50)
}
</script>

4.4 动画添加回调函数

  • 函数可以作为一个参数传到另一个函数中去,当那个函数执行完后,再执行传进去的这个函数。

  • 回调函数写在函数结束位置

var div = document.querySelector('div');
//简单动画函数封装obj 目标对象 target 目标位置 callback 函数
function an(obj, target, callback) {
clearInterval(obj.timer);
obj.timer = setInterval(function () {
if (obj.offsetLeft == target) {
clearInterval(obj.timer);
if (callback) {
callback();
}
}
var x = (target - obj.offsetLeft) / 10;
x = x > 0 ? Math.ceil(x) : Math.floor(x);
obj.style.left = obj.offsetLeft + x + 'px';
}, 50)
}
var btn800 = document.querySelector('.btn800');
btn800.addEventListener('click', function () {
an(div, 800, function () {
div.style.backgroundColor = 'skyblue'
});
})

5. 常见网页特效案例

5.1 轮播图

  • 无缝滚动的原理:
  • 将第一张图克隆一张放到最后,
  • 当图片滚动到克隆的最后一张图片时,让ul不做动画的跳到最左侧:left:0;
  • 同时num=0
点击查看代码
<style>
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
.slider {
overflow: hidden;
width: 400px;
height: 223.16px;
background-color: aqua;
margin: 50px auto;
position: relative;
}
.leftbotton,
.rightbotton {
position: absolute;
top: 50%;
margin-top: -25px;
}
.leftbotton {
left: 0;
}
.rightbotton {
right: 0;
}
.bottombotton {
width: 100px;
height: 20px;
background-color: rgba(255, 255, 255, 0.2);
border-radius: 10px;
position: absolute;
bottom: 8px;
left: 50%;
margin-left: -50px;
}
.bottombotton li {
width: 8px;
height: 8px;
border: 1px solid #b7b7b7;
float: left;
margin: 4px 5px;
border-radius: 50%;
}
.current {
background-color: rgb(219, 91, 91);
}
.imges {
width: 600%;
position: absolute;
}
.imges li {
float: left;
}
.btn {
display: none;
}
</style>
<!-- animate.js 必须在index的上面 -->
<script src="../animate.js"></script>
<script src="index.js"></script>
</head>
<body>
<div class="slider">
<!-- 滑动图片 -->
<ul class="imges">
<li><img src="../imges/qiche.jpg" alt="" width="400px"></li>
<li><img src="../imges/qiche.jpg" alt="" width="400px"></li>
<li><img src="../imges/qiche.jpg" alt="" width="400px"></li>
<li><img src="../imges/qiche.jpg" alt="" width="400px"></li>
<li><img src="../imges/qiche.jpg" alt="" width="400px"></li>
</ul>
<!-- 左右按钮 -->
<div class="btn">
<a href="javascript:;" class="leftbotton"><img src="../imges/leftjt.jpeg" alt="" width="50px"
height="50px"></a>
<a href="javascript:;" class="rightbotton"><img src="../imges/rightjt.jpg" alt="" width="50px"
height="50px"></a>
</div>
<ul class="bottombotton">
</ul>
</div>
</body><style>
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
.slider {
overflow: hidden;
width: 400px;
height: 223.16px;
background-color: aqua;
margin: 50px auto;
position: relative;
}
.leftbotton,
.rightbotton {
position: absolute;
top: 50%;
margin-top: -25px;
}
.leftbotton {
left: 0;
}
.rightbotton {
right: 0;
}
.bottombotton {
width: 100px;
height: 20px;
background-color: rgba(255, 255, 255, 0.2);
border-radius: 10px;
position: absolute;
bottom: 8px;
left: 50%;
margin-left: -50px;
}
.bottombotton li {
width: 8px;
height: 8px;
border: 1px solid #b7b7b7;
float: left;
margin: 4px 5px;
border-radius: 50%;
}
.current {
background-color: rgb(219, 91, 91);
}
.imges {
width: 600%;
position: absolute;
}
.imges li {
float: left;
}
.btn {
display: none;
}
</style>
<!-- animate.js 必须在index的上面 -->
<script src="../animate.js"></script>
<script src="index.js"></script>
</head>
<body>
<div class="slider">
<!-- 滑动图片 -->
<ul class="imges">
<li><img src="../imges/qiche.jpg" alt="" width="400px"></li>
<li><img src="../imges/qiche.jpg" alt="" width="400px"></li>
<li><img src="../imges/qiche.jpg" alt="" width="400px"></li>
<li><img src="../imges/qiche.jpg" alt="" width="400px"></li>
<li><img src="../imges/qiche.jpg" alt="" width="400px"></li>
</ul>
<!-- 左右按钮 -->
<div class="btn">
<a href="javascript:;" class="leftbotton"><img src="../imges/leftjt.jpeg" alt="" width="50px"
height="50px"></a>
<a href="javascript:;" class="rightbotton"><img src="../imges/rightjt.jpg" alt="" width="50px"
height="50px"></a>
</div>
<ul class="bottombotton">
</ul>
</div>
</body>

index.js:

window.addEventListener('load', function () {
var slider = document.querySelector('.slider');
var btn = document.querySelector('.btn');
var btnleft = document.querySelector('.leftbotton');
var btnright = document.querySelector('.rightbotton');
var imges = document.querySelector('.imges');
var botes = document.querySelector('.bottombotton');
var sliderwidth = slider.offsetWidth;
//鼠标放在轮播图里,显示左右箭头,并停止自动播放的定时器
slider.addEventListener('mouseover', function () {
btn.style.display = 'block';
clearInterval(timer);
timer = null;//不用就清空定时器变量
})
slider.addEventListener('mouseout', function () {
btn.style.display = '';
//开始定时器
timer = setInterval(function () {
//手动调用按钮点击事件 元素名.click()
btnright.click();
}, 2000)
})
//动态生成小圆圈,创建子节点
for (var i = 0; i < imges.children.length; i++) {
var li = document.createElement('li');
botes.appendChild(li);
//设置索引号(设置索引号而不直接使用 i,是因为点击事件发生时,循环已经执行完毕了)
//循环执行时 会给每个li都设置index值,第一个点的index=0,这样调用index,就知道是第几个点了)
li.setAttribute('index', i);
li.addEventListener('click', function () {
for (var i = 0; i < botes.children.length; i++) {
botes.children[i].className = '';
}
this.className = 'current';
//点击圆点移动图片
var index = this.getAttribute('index');
//当我们点击圆圈时,应该把这个圆圈的索引号传给num和circle,这样左右按钮点击时就是从这张图开始动
//index是圆点控制图片的索引,num是左右两按钮控制图片的索引,circle是图片控制圆点的索引
num = index;
circle = index;
animate(imges, -(index * sliderwidth));
})
}
botes.children[0].className = 'current';
//点击两侧按钮,图片滚动
//克隆第一张图片,放到最后
var liclone = imges.children[0].cloneNode(true);
imges.appendChild(liclone);//(因为是先生成圆圈再克隆,因此上面的圆圈不会有变化)
var num = 0//每点击一次,num+1
var circle = 0//控制小圆圈的播放
btnright.addEventListener('click', function () {
//如果走到最后复制的一张,left:0
if (num == imges.children.length - 1) {
imges.style.left = 0;
num = 0;
}
num++;
animate(imges, -(num * sliderwidth))
//小圆圈随图片变化
circle++;
if (circle == botes.children.length) {
circle = 0;//图片比圆圈多一个,因此到最后一个圆圈时,再从第一个圆圈开始
}
change();
})
btnleft.addEventListener('click', function () {
//如果走到第一张,快速跳到最后一张克隆的图片
if (num == 0) {
num = imges.children.length - 1;
imges.style.left = -num * sliderwidth + 'px';
}
num--;
animate(imges, -(num * sliderwidth))
//小圆圈随图片变化
circle--;
if (circle < 0) {
circle = botes.children.length - 1;//图片比圆圈多一个,因此到第一个时,再从最后一个圆圈开始
}
change();
})
//小圆圈的样式变化
function change() {
for (var i = 0; i < botes.children.length; i++) {
botes.children[i].className = '';
}
botes.children[circle].className = 'current';
}
//自动播放(相当于点击了右侧按钮)
var timer = setInterval(function () {
//手动调用按钮点击事件 元素名.click()
btnright.click();
}, 2000)
})

5.2 节流阀

  • 防止轮播图按钮连续点击造成播放过快。

  • 节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。

  • 核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。

  • 开始设置一个变量var flag= true;

  • If(flag){flag = false; do something} 关闭水龙头

  • 利用回调函数动画执行完毕, flag = true 打开水龙头

以点击右键事件为例

var flag = true;//节流阀
btnright.addEventListener('click', function () {
if (flag) {
flag = false;//关闭节流阀
//如果走到最后复制的一张,left:0
if (num == imges.children.length - 1) {
imges.style.left = 0;
num = 0;
}
num++;
animate(imges, -(num * sliderwidth),function(){
flag=true;//打开节流阀
})
//小圆圈随图片变化
circle++;
if (circle == botes.children.length) {
circle = 0;//图片比圆圈多一个,因此到最后一个圆圈时,再从第一个圆圈开始
}
change();
}
})

5.3 返回顶部

滚动窗口至文档中的特定位置

window.scroll(x,y)//x,y不加单位

点击查看代码
<body>
<div class="slider_bar"><span>返回顶部</span></div>
<div class="header">头部区域</div>
<div class="banner">banner区域</div>
<div class="main">主体部分</div>
<script>
var bar = document.querySelector('.slider_bar');
var banner = document.querySelector('.banner');
var main = document.querySelector('.main');
var back = document.querySelector('span');
//banner 距离页面上边框的距离
var bannertop = banner.offsetTop;
//侧边栏 距离页面上边框的距离
var bartop = bar.offsetTop;
//主体部分 距离页面上边框的距离
var maintop = main.offsetTop;
document.addEventListener('scroll', function () {
//获取页面上卷距离用 window.pageYoffset
var x = window.pageYOffset;
//当页面拖到 banner时,侧边栏不动
if (x >= bannertop) {
bar.style.position = 'fixed';
bar.style.top = bartop - bannertop + 'px';
} else {
bar.style.position = '';
bar.style.top = '';
}
//当页面拖到 主体部分时,显示返回顶部
if (x >= maintop) {
back.style.display = 'block';
} else {
back.style.display = '';
}
})
//返回顶部事件
back.addEventListener('click', function () {
animate(window, 0)
})
function animate(obj, target, callback) {
clearInterval(obj.timer);
obj.timer = setInterval(function () {
if (obj.pageYOffset == target) {
clearInterval(obj.timer);
/* if (callback) {
callback();
} */
callback && callback()//相当于上边的if语句
}
//y为这次应该走的距离=(目标值-现在的位置)/10
//这里要将步长值取整数(向上取整)
//考虑到向后退,因此当步长为负时要想下取整
var y = (target - obj.pageYOffset) / 10;
y = y > 0 ? Math.ceil(y) : Math.floor(y);
// obj.style.top = obj.pageYOffset + y + 'px';
window.scroll(0, window.pageYOffset + y);
}, 30)
}
</script>

5.4 筋斗云

  • 鼠标经过某个小li, 筋斗云跟这到当前小li位置

  • 鼠标离开这个小li, 筋斗云复原为原来的位置

  • 鼠标点击了某个小li, 筋斗云就会留在点击这个小li 的位置

案例分析:

  • 利用动画函数做动画效果
  • 原先筋斗云的起始位置是0
  • 鼠标经过某个小li,把当前小li的offsetLeft 位置做为目标值即可
  • 鼠标离开某个小li,就把目标值设为 0
  • 如果点击了某个小li, 就把li当前的位置存储起来,做为筋斗云的起始位置

html+css:

点击查看代码
<style>
* {
margin: 0;
padding: 0;
}
.c_nav {
position: relative;
width: 700px;
margin: 100px auto;
margin-bottom: 0;
}
.tab {
position: absolute;
top: 0;
left: 0;
border-bottom: 2px solid rgb(220, 63, 63);
line-height: 35px;
}
.tab li {
list-style: none;
text-align: center;
padding: 0 15px;
float: left;
}
.cloud {
width: 94px;
height: 35px;
position: absolute;
top: 0;
left: 0;
background-color: rgb(220, 63, 63, .3);
border-radius: 50px 50px;
z-index: -1;
}
.chance {
color: steelblue;
}
</style>
<script src="animate.js"></script>
</head>
<body>
<div class="c_nav">
<span class="cloud"></span>
<ul class="tab">
<li class="chance">商品介绍</li>
<li>规格与包装</li>
<li>售后保障</li>
<li>商品评价</li>
<li>手机社区</li>
<li>东倒西歪</li>
<li>天气很热</li>
</ul>
</div>

js部分:

<script>
var cloud = document.querySelector('.cloud');
var c_nav = document.querySelector('.c_nav');
var lis = document.querySelectorAll('li');
var current = 0//筋斗云固定要返回的位置
for (var i = 0; i < lis.length; i++) {
lis[i].addEventListener('mouseenter', function () {
animate(cloud, this.offsetLeft)//距离就是当前li的位置
})
lis[i].addEventListener('mouseleave', function () {
animate(cloud, current)//筋斗云返回固定的位置
})
//鼠标点击,将当前位置作为目标值
lis[i].addEventListener('click', function () {
current = this.offsetLeft;//筋斗云返回的固定位置是鼠标点击的 li的位置
cloud.style.left = current;
//排他,将所有其他的li的字体颜色改为默认颜色
for (var i = 0; i < lis.length; i++) {
lis[i].className = '';
}
this.className = 'chance'
})
}
</script>

移动端网页特效

1. 触屏事件

touch(也称触摸事件),Android 和 IOS 都有。

触屏touch事件 说明
touchstart 手指触摸到一个DOM元素时触发
touchmove 手指在一个DOM元素上滑动时触发
touchend 手指从一个DOM元素上移开时触发

1.1触摸事件对象

触摸事件对象重点看三个常见对象列表:

触摸列表 说明
touches 正在触摸屏幕的手指的列表
targetTouches(常用) 正在触摸当前DOM元素上的手指的列表
changedTouches 手指状态发生了改变的列表,从无到有,从有到无变化
div.addEventListener('touchstart', function (e) {
console.log(e);
//触摸屏幕会有touchestouches列表,targetTouches列表和changedTouches列表
})
div.addEventListener('touchend', function (e) {
console.log(e);
})
//当时手指离开屏幕的时候,就没有了 touches和targetTouches列表
//但是会有changedTouches

1.2 移动端拖动元素

  • touchstart、touchmove、touchend可以实现拖动元素

  • 但是拖动元素需要当前手指的坐标值,可以使用targetTouches[0]里面的pageX和pageY

  • 移动端拖动的原理:手指移动中,计算出手指移动的距离。然后用盒子原来的位置+手指移动的距离。

  • 手指移动的距离:手指滑动中的位置减去手指刚开始触摸的位置

<script>
var div = document.querySelector('div');
var x0 = 0;//要设计为全局变量
var y0 = 0;
var leftx = 0;
var topy = 0;
div.addEventListener('touchstart', function (e) {
x0 = e.targetTouches[0].pageX;//手指触摸开始位置
y0 = e.targetTouches[0].pageY;
leftx = this.offsetLeft;//元素开始位置的边距
topy = this.offsetTop;
})
div.addEventListener('touchmove', function (e) {
var x = e.targetTouches[0].pageX - x0;//手指现在的位置-开始位置=偏移量
var y = e.targetTouches[0].pageY - y0;
div.style.left = x + leftx + 'px';//开始边距+偏移量=现在边距
div.style.top = y + topy + 'px';
})
e.preventDefault(); //阻止屏幕滚动的默认行为
</script>

2. 移动端常见特效

2.1 轮播图

检测过渡完成事件:transitionend

classList属性

  • classList属性是HTML5新增的一个属性,返回元素的类名。但是ie10以上版本支持。

  • 该属性用于在元素中添加,移除及切换 CSS 类。
    有以下方法:

    • 添加类:

    element.classList.add(’类名’);

    • 移除类:

    element.classList.remove(’类名’);

    • 切换类:

    element.classList.toggle(’类名’);

    • 注意以上方法里面,所有类名都不带点。

2.2 click延时解决方案

移动端click事件会有300ms的延时,原因是移动端屏幕双击会缩放(double tap to zoom)页面。

解决方案:

  • 1、禁用缩放
    浏览器禁用默认的双击缩放行为并且去掉300ms的点击延迟。

meta name="viewport" content="user-scalable=no">

  • 2、利用touch事件封装这个事件解决300ms延迟

    • 当手指触摸屏幕,记录当前触摸事件
    • 当手指离开屏幕,用离开的时间减去触摸的事件
    • 如果时间小于150ms,并且没有滑动过屏幕,那么就定义为点击

  • 3、利用fastclick.js插件
if('addEventListener' in document){
document.addEventListener('DOMContentLoaded',function(){
FastClick.attach(document.body);
},false)
}

2.3 Swiper插件

中文官网地址: https://www.swiper.com.cn/

  1. 引入插件相关文件
    swiper -> dist -> css&js文件->swiper.min.css&swiper.min.js
  2. 按照规定语法使用
    复制 html结构,css样式,js

3. 移动端常见插件

  • superslide: http://www.superslide2.com/

    网站上常用的“焦点图/幻灯片”“Tab标签切换”“图片滚动”“无缝滚动”等只需要一个SuperSlide即可解决!
    从此无需网上苦苦寻觅特效,无需加载n个插件,无需害怕代码冲突,你需要的只是一个SuperSlide!

  • iscroll: https://github.com/cubiq/iscroll

3.1 移动端视频插件 zy.media.js

H5 给我们提供了 video 标签,但是浏览器的支持情况不同。
不同的视频格式文件,我们可以通过source 解决。
但是外观样式,还有暂停,播放,全屏等功能我们只能自己写代码解决。
这个时候我们可以使用插件方式来制作。

4. 移动端常见框架

  • 前端常用的框架有 Bootstrap、Vue、Angular、React 等。既能开发PC端,也能开发移动端

  • 前端常用的移动端插件有 swiper、superslide、iscroll 等。

Bootstrap

  • Bootstrap 是一个简洁、直观、强悍的前端开发框架,它让 web 开发更迅速、简单。

  • 它能开发PC端,也能开发移动端

    Bootstrap JS插件使用步骤:

    • 引入相关js 文件
    • 复制HTML 结构
    • 修改对应样式
    • 修改相应JS 参数

5. 本地存储

随着互联网的快速发展,基于网页的应用越来越普遍,同时也变的越来越复杂,为了满足各种各样的需求,会经 常性在本地存储大量的数据,HTML5规范提出了相关解决方案。

本地存储特性

  • 数据存储在用户浏览器中

  • 设置、读取方便、甚至页面刷新不丢失数据

  • 容量较大,sessionStorage约5M、localStorage约20M

  • 只能存储字符串,可以将对象JSON.stringify() 编码后存储

5.1 window.sessionStorage

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

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

  • 以键值对的形式存储使用

存储数据:

sessionStorage.setItem(key, value)

获取数据:

sessionStorage.getItem(key)

删除数据:

sessionStorage.removeItem(key)

删除所有数据:

sessionStorage.clear()

数据查看:应用->存储->会话存储空间

<input type="text">
<button class="set">存储数据</button>
<button class="get">获取数据</button>
<button class="remove">删除数据</button>
<button class="del">清空所有数据</button>
<script>
var ipt = document.querySelector('input');
var set = document.querySelector('.set');
var get = document.querySelector('.get');
var del = document.querySelector('.del');
var remove = document.querySelector('.remove');
set.addEventListener('click', function () {
var valu = ipt.value;
sessionStorage.setItem('uname', valu);
sessionStorage.setItem('pwd', valu);
})
get.addEventListener('click', function () {
alert(sessionStorage.getItem('uname'));//获取表单的值
})
remove.addEventListener('click', function () {
sessionStorage.removeItem('uname')//删除uname
})
del.addEventListener('click', function () {
sessionStorage.clear();//删除全部
})
</script>

5.2 window.localStorage

  • 声明周期永久生效,除非手动删除 否则关闭页面也会存在

  • 可以多窗口(页面)共享(同一浏览器可以共享)

  • 以键值对的形式存储使用

存储数据:

localStorage.setItem(key, value)

获取数据:

localStorage.getItem(key)

删除数据:

localStorage.removeItem(key)

删除所有数据:

localStorage.clear()

数据查看:应用->存储->本地存储空间

set.addEventListener('click', function () {
var valu = ipt.value;
localStorage.setItem('uname', valu);
localStorage.setItem('pwd', valu);
})
get.addEventListener('click', function () {
alert(localStorage.getItem('uname'));//获取表单的值
})
remove.addEventListener('click', function () {
localStorage.removeItem('uname')//删除uname
})
del.addEventListener('click', function () {
localStorage.clear();//删除全部
})

案例:记住用户名
如果勾选记住用户名, 下次用户打开浏览器,就在文本框里面自动显示上次登录的用户名

案例分析:
① 把数据存起来,用到本地存储
② 关闭页面,也可以显示用户名,所以用到localStorage
③ 打开页面,先判断是否有这个用户名,如果有,就在表单里面显示用户名,并且勾选复选框
④ 当复选框发生改变的时候 change事件
⑤ 如果勾选,就存储,否则就移除
if(this,checked){}

posted @   准备开始  阅读(57)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示