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/
- 引入插件相关文件
swiper -> dist -> css&js文件->swiper.min.css&swiper.min.js - 按照规定语法使用
复制 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){}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?