JavaScript 进阶5:WebAPI:DOM- 网页特效篇
Date: January 28, 2023
Text: 轮播图高级版待解决
滚动事件、加载事件、元素大小和位置(scroll、offset、client)、轮播图案例
目标
- 能够制作常见的网页特效
- 具备制作常见网页焦点图的能力
滚动事件和加载事件
滚动事件
概念:当页面进行滚动时触发的事件
为什么要学?
很多网页需要检测用户把页面滚动到某个区域后做一些处理, 比如固定导航栏,比如返回顶部
事件名:scroll
监听整个页面滚动:
// 页面滚动事件
window.addEventListener('scroll', function() {})
给 window 或 document 添加 scroll 事件
监听某个元素的内部滚动直接给某个元素加即可
案例:
-
Code:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> body { height: 3000px; } div { overflow: auto; width: 200px; height: 200px; background-color: pink; } </style> </head> <body> <div> 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 </div> <script> let div = document.querySelector('div') window.addEventListener('scroll', function () { console.log(111) }) // div.addEventListener('scroll', function () { // console.log(111) // }) </script> </body> </html>
加载事件
加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件
为什么要学?
有些时候需要等页面资源全部处理完了做一些事情
老代码喜欢把 script 写在 head 中,这时候直接找 dom 元素找不到
事件名:load
监听页面所有资源加载完毕:
给 window 添加 load 事件
// 页面加载事件
window.addEventListener('load', function() {
// 执行的操作
})
注意:不光可以监听整个页面资源加载完毕,也可以针对某个资源绑定load事件
当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像等完全加载
事件名:DOMContentLoaded
监听页面DOM加载完毕:
给 document 添加 DOMContentLoaded 事件
document.addEEventListener('DOMContentLoaded', function () {
// 执行的操作
})
案例:
-
Code:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> body { height: 3000px; } div { overflow: auto; width: 200px; height: 200px; background-color: pink; } </style> <script> window.addEventListener('load', function () { let div = document.querySelector('div') console.log(div) }) </script> </head> <body> <div> 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 我里面可以放很多的文字 </div> <script> // let div = document.querySelector('div') </script> </body> </html>
总结:
-
页面滚动事件如何添加?
scroll
监听整个页面滚动给 window 或 document 加 -
加载事件有哪两个?如何添加?
-
load 事件
监听整个页面资源给 window 加
-
-
DOMContentLoaded
- 给 document 加,当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像等完全加载
元素大小和位置
目标:掌握元素大小和位置的获取方法,为后续网页特效打基础
scroll家族
使用场景:
我们想要页面滚动一段距离,比如100px,就让某些元素显示隐藏,那我们怎么知道,页面滚动了
100像素呢?就可以使用scroll 来检测页面滚动的距离~~~
获取宽高:
获取元素的内容总宽高(不包含滚动条)返回值不带单位
scrollWidth和scrollHeight
获取位置:
获取元素内容往左、往上滚出去看不到的距离
scrollLeft和scrollTop
这两个属性是可以修改的
div.addEventListener('scroll', function () {
console.log(this.scrollTop)
})
开发中,我们经常检测页面滚动的距离,比如页面滚动100像素,就可以显示一个元素,或者固定一个元素
// 页面滚动事件
window.addEventListener('scroll', function() {
// document.documnetElement.scrollTop 获得当前页面被卷去的头部
let num = document.documentElement.scrollTop
console.log(num)
document.documentElement.scrollTop = 500 //注意这里没有单位
}
注意事项:
document.documentElement HTML文档返回对象为HTML元素
案例:
-
Code:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> body { height: 3000px; } </style> </head> <body> <script> console.log(document.documentElement) // 返回 html 元素 // 可以修改 但是不要带单位 // 先做页面滚动事件 window.addEventListener('scroll', function () { // console.log(11) // 在得到页面滚动的距离 scrollTop console.log(document.documentElement.scrollTop) // document.documentElement.scrollTop = 500 }) </script> </body> </html>
总结:
-
scrollWidth和scrollHeight是得到元素什么的宽高?
内容
不包含滚动条 -
被卷去的头部或者左侧用那个属性?是否可以读取和修改?
scrollTop / scrollLeft
可以读取,也可以修改(赋值) -
检测页面滚动的头部距离(被卷去的头部)用那个属性?
document.documentElement.scrollTop
案例:页面滚动显示返回顶部按钮
需求:当页面滚动500像素,就显示返回顶部按钮,否则隐藏, 同时点击按钮,则返回顶部
分析:
①:用到页面滚动事件
②:检测页面滚动大于等于100像素,则显示按钮
③:点击按钮,则让页面的scrollTop 重置为 0
-
Code:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } .content { width: 1000px; height: 3000px; background-color: pink; margin: 0 auto; } .backtop { display: none; width: 50px; left: 50%; margin: 0 0 0 505px; position: fixed; bottom: 60px; z-index: 100; } .backtop a { height: 50px; width: 50px; background: url(./images/bg2.png) 0 -600px no-repeat; opacity: 0.35; overflow: hidden; display: block; text-indent: -999em; cursor: pointer; } </style> </head> <body> <div class="content"></div> <div class="backtop"> <img src="./images/close2.png" alt=""> <a href="javascript:;"></a> </div> <script> // 0 获取元素 let backtop = document.querySelector('.backtop') // 一. 页面滚动事件 window.addEventListener('scroll', function () { // 2. 页面检测滚动的距离 // console.log(document.documentElement.scrollTop) let num = document.documentElement.scrollTop // 3. 进行判断显示和隐藏 if (num >= 500) { //显示那个元素 backtop.style.display = 'block' } else { // 否则隐藏元素 backtop.style.display = 'none' } }) // 二、点击链接返回顶部 backtop.children[1] backtop.children[1].addEventListener('click', function () { // 返回顶部 // scrollTop 可读写 document.documentElement.scrollTop = 0 }) </script> </body> </html>
offset家族
使用场景:
前面案例滚动多少距离,都是我们自己算的,最好是页面
滚动到某个元素,就可以做某些事。
简单说,就是通过js的方式,得到元素在页面中的位置
这样我们可以做,页面滚动到这个位置,就可以返回顶部的小盒子显示…
获取宽高:
获取元素的自身宽高、包含元素自身设置的宽高、padding、border
offsetWidth和offsetHeight
获取位置:
获取元素距离自己定位父级元素的左、上距离
offsetLeft和offsetTop 注意是只读属性
总结:
-
offsetWidth和offsetHeight是得到元素什么的宽高?
内容 + padding + border
-
offsetTop和offsetLeft 得到位置以谁为准?
带有定位的父级
如果都没有则以 文档左上角 为准
案例:仿京东固定导航栏
需求:当页面滚动到秒杀模块,导航栏自动滑入,否则滑出
分析:
①:用到页面滚动事件
②:检测页面滚动大于等于 秒杀模块的位置 则滑入,否则滑出
-
Code:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } .content { overflow: hidden; width: 1000px; height: 3000px; background-color: pink; margin: 0 auto; } .backtop { display: none; width: 50px; left: 50%; margin: 0 0 0 505px; position: fixed; bottom: 60px; z-index: 100; } .backtop a { height: 50px; width: 50px; background: url(./images/bg2.png) 0 -600px no-repeat; opacity: 0.35; overflow: hidden; display: block; text-indent: -999em; cursor: pointer; } .header { position: fixed; top: -80px; left: 0; width: 100%; height: 80px; background-color: purple; text-align: center; color: #fff; line-height: 80px; font-size: 30px; transition: all .3s; } .sk { width: 300px; height: 300px; background-color: skyblue; margin-top: 600px; } </style> </head> <body> <div class="header">我是顶部导航栏</div> <div class="content"> <div class="sk">秒杀模块</div> </div> <div class="backtop"> <img src="./images/close2.png" alt=""> <a href="javascript:;"></a> </div> <script> let sk = document.querySelector('.sk') let header = document.querySelector('.header') // 1. 页面滚动事件 window.addEventListener('scroll', function () { // console.log(11) // 要检测滚动的距离 // console.log(document.documentElement.scrollTop) // console.log(sk.offsetTop) // 2. 要检测滚动的距离 >= 秒杀模块的offsetTop 则滑入 if (document.documentElement.scrollTop >= sk.offsetTop) { // alert('改吃饭了') header.style.top = '0' } else { header.style.top = '-80px' } }) </script> </body> </html>
案例:电梯导航
需求:点击可以页面调到指定效果
分析:
①:点击当前 小导航,当前添加active,其余移除active
②:得到对应 内容 的 offsetTop值
③:让页面的 scrollTop 走到 对应 内容 的 offsetTop
-
Code:
<!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; } body { height: 3000px; } .aside { position: fixed; left: 0; top: 50%; transform: translateY(-50%); } .item { height: 40px; line-height: 40px; text-align: center; padding: 0 10px; cursor: pointer; } .active { background-color: red; color: #fff; } .content { width: 660px; margin: 400px auto; } .neirong { height: 300px; margin-bottom: 20px; color: #fff; } .content1 { background-color: red; } .content2 { background-color: blue; } .content3 { background-color: orange; } .content4 { background-color: yellowgreen; } </style> </head> <body> <div class="aside"> <div class="item active">男装/女装</div> <div class="item">儿童服装/游乐园</div> <div class="item">电子产品</div> <div class="item">电影/美食</div> </div> <div class="content"> <div class="neirong content1">男装/女装</div> <div class="neirong content2">儿童服装/游乐园</div> <div class="neirong content3">电子产品</div> <div class="neirong content4">电影/美食</div> </div> <script> // 1. 获元取素 let items = document.querySelectorAll('.item') // 内容的盒子获取 let neirongs = document.querySelectorAll('.neirong') // 2. 左侧aside 模块 点击谁,谁高亮 for (let i = 0; i < items.length; i++) { items[i].addEventListener('click', function () { // 找到上一个active 移除类 document.querySelector('.aside .active').classList.remove('active') // 点击谁谁添加类 this.classList.add('active') // 3. 右侧内容跟随走动 让页面滚动到对应的offsetTop值位置 // console.log(neirongs[i].offsetTop) 不用给单位 document.documentElement.scrollTop = neirongs[i].offsetTop }) } </script> </body> </html>
client家族
获取宽高:
获取元素的可见部分宽高(包含padding,不包含边框,滚动条等)
clientWidth和clientHeight
获取位置:
获取左边框和上边框宽度clientLeft和clientTop 注意是只读属性
会在窗口尺寸改变的时候触发事件:resize
window.addEventListener('resize', function () {
// 执行的代码
})
检测屏幕宽度:
window.addEventListener('resize', funtion () {
let w = document.documentElement.clientWidth
console.log(w)
})
总结:
- offset家族
获取元素自身大小:包括自身设置的宽高、padding、border
获取元素距离定位父级的左和上距离 只读属性
- client家族
获取元素可见区域的大小
获取元素左、上边框距离 只读属性
- scroll家族
获取元素内容的总大小
获取元素向左向上滚出去看不见的距离 可读写属性
综合案例
轮播图案例
效果:
需求①:小图标鼠标经过事件
鼠标经过小图片,当前高亮,其余兄弟变淡 添加类
需求② :大图片跟随变化
对应的大图片跟着显示,如果想要过渡效果,可以使用opacity效果,可以利用CSS淡入淡出的效果,还是添加类
需求③:右侧按钮播放效果
点击右侧按钮,可以自动播放下一张图片
需要一个变化量 index 不断自增
然后播放下一张图片
如果到了最后一张,必须要还原为第1张图片
教你一招: 索引号 = 索引号 % 数组长度 (放到播放前面)
需求④:解决一个BUG
点击右侧按钮可以实现播放下一张,但是鼠标经过前面的,播放就会乱序
解决方案: 让变化量(索引号) 重新赋值为 当前鼠标经过的索引号
需求⑤:左侧按钮播放效果
点击左侧按钮,可以自动播放上一张图片
需要一个变化量 index 不断自减
然后播放上一张图片
如果到了第一张,必须要从最后一张播放
教你一招: 索引号 = (数组长度 + 索引号) % 数组长度
需求⑥:
因为左侧按钮和右侧按钮里面有大量相同的操作,可以抽取封装一个函数 common
需求⑦:开启定时器
其实定时器自动播放,就相当于点击了右侧按钮,此时只需要, right.click()
需求⑧:
鼠标经过停止定时器 (清除定时器)
鼠标离开开启定时器 (开启定时器)
-
Code:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>QQ音乐轮播图</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } li { list-style: none; } .main { width: 700px; margin: auto; background: #000; } .slides { height: 320px; position: relative; } .slides ul li { /* display: none; */ position: absolute; top: 0; left: 0; opacity: 0; /* 这里实现淡入淡出的关键 */ transition: all .3s; } .slides li.active { /* display: block; */ opacity: 1; } .slides .extra { width: 700px; height: 53px; line-height: 53px; position: absolute; bottom: 0px; background-color: rgba(0, 0, 0, 0.8); z-index: 10; } .slides .extra h3 { width: 82%; margin: 0; margin-right: 20px; padding-left: 20px; color: #98E404; font-size: 28px; float: left; font-weight: 500; font-family: "Microsoft Yahei", Tahoma, Geneva; } .slides .extra a { width: 30px; height: 29px; display: block; float: left; margin-top: 12px; margin-right: 3px; background-image: url(./assets/icon_focus_switch.png); } .slides .extra .prev { background-position: 0 0; } .slides .extra .prev:hover { background-position: -30px 0; } .slides .extra .next { background-position: -60px 0; } .slides .extra .next:hover { background-position: -90px 0; } .indicator { padding: 10px 0; } .indicator ul { list-style-type: none; margin: 0 0 0 4px; padding: 0; overflow: hidden; } .indicator ul li { position: relative; float: left; width: 60px; margin: 0 4px 0 5px; text-align: center; cursor: pointer; } .indicator li img { display: block; border: 0; text-align: center; width: 60px; } .indicator li .mask { width: 60px; height: 60px; position: absolute; top: 0; left: 0; background-color: rgba(0, 0, 0, 0.4); } .indicator li .border { display: none; width: 54px; position: absolute; bottom: 0; left: 0; z-index: 20; border: 3px solid #98E404; } /* li里面的mask 和 border 刚开始都是显示的 */ /* 我们写一个样式css */ .indicator .active .mask { display: none; } .indicator .active .border { display: block; } </style> </head> <body> <div class="main"> <div class="slides"> <ul> <li class="active"><a href="#"><img src="./assets/b_01.jpg" alt="第1张图的描述信息"></a></li> <li><a href="#"><img src="./assets/b_02.jpg" alt="第2张图的描述信息"></a></li> <li><a href="#"><img src="./assets/b_03.jpg" alt="第3张图的描述信息"></a></li> <li><a href="#"><img src="./assets/b_04.jpg" alt="第4张图的描述信息"></a></li> <li><a href="#"><img src="./assets/b_05.jpg" alt="第5张图的描述信息"></a></li> <li><a href="#"><img src="./assets/b_06.jpg" alt="第6张图的描述信息"></a></li> <li><a href="#"><img src="./assets/b_07.jpg" alt="第7张图的描述信息"></a></li> <li><a href="#"><img src="./assets/b_08.jpg" alt="第8张图的描述信息"></a></li> <li><a href="#"><img src="./assets/b_09.jpg" alt="第9张图的描述信息"></a></li> <li><a href="#"><img src="./assets/b_10.jpg" alt="第9张图的描述信息"></a></li> </ul> <div class="extra"> <h3>第1张图的描述信息</h3> <a class="prev" href="javascript:;"></a> <a class="next" href="javascript:;"></a> </div> </div> <div class="indicator"> <ul> <li class="active"> <img src="assets/s_01.jpg"> <span class="mask"></span> <span class="border"></span> </li> <li> <img src="assets/s_02.jpg"> <span class="mask"></span> <span class="border"></span> </li> <li> <img src="assets/s_03.jpg"> <span class="mask"></span> <span class="border"></span> </li> <li> <img src="assets/s_04.jpg"> <span class="mask"></span> <span class="border"></span> </li> <li> <img src="assets/s_05.jpg"> <span class="mask"></span> <span class="border"></span> </li> <li> <img src="assets/s_06.jpg"> <span class="mask"></span> <span class="border"></span> </li> <li> <img src="assets/s_07.jpg"> <span class="mask"></span> <span class="border"></span> </li> <li> <img src="assets/s_08.jpg"> <span class="mask"></span> <span class="border"></span> </li> <li> <img src="assets/s_09.jpg"> <span class="mask"></span> <span class="border"></span> </li> <li> <img src="assets/s_10.jpg"> <span class="mask"></span> <span class="border"></span> </li> </ul> </div> </div> <script> // 轮播图开始啦 // 需求①:小图标鼠标经过事件 // 鼠标经过小图片,当前高亮,其余兄弟变淡 添加类 let lis = document.querySelectorAll('.indicator li') let piclis = document.querySelectorAll('.slides ul li') let text = document.querySelector('.extra h3') let next = document.querySelector('.next') let prev = document.querySelector('.prev') let main = document.querySelector('.main') // 给多个小li绑定事件 for (let i = 0; i < lis.length; i++) { lis[i].addEventListener('mouseenter', function () { // 选出唯一的那个active ,删除类 document.querySelector('.indicator .active').classList.remove('active') // 鼠标经过谁,谁加上active 这个类 this.classList.add('active') // 需求② :大图片跟随变化 一定要放到鼠标经过事件里面 // 对应的大图片跟着显示,如果想要过渡效果,可以使用opacity效果,可以利用CSS淡入 淡出的效果,还是添加类 // 选出唯一的那个active ,删除类 document.querySelector('.slides ul .active').classList.remove('active') // 对应序号的那个 li,谁加上active 这个类 piclis[i].classList.add('active') text.innerHTML = `第${i + 1}张图的描述信息` // 需求④:解决一个BUG // 点击右侧按钮可以实现播放下一张,但是鼠标经过前面的,播放就会乱序 // 解决方案: 让变化量 index 重新赋值为 当前鼠标经过的索引号 // 鼠标经过了那个小li 他的索引号就是 i // 右侧按钮是通过 index 来了控制播放的 index = i }) } // 需求③:右侧按钮播放效果 // 点击右侧按钮,可以自动播放下一张图片 // 需要一个变化量 index 不断自增 // 然后播放下一张图片 // 如果到了最后一张,必须要还原为第1张图片 // 教你一招: 索引号 = 索引号 % 数组长度 (放到播放前面) let index = 0 // 全局变量 信号量 控制器 为了给 右侧按钮和左侧按钮同时使用 next.addEventListener('click', function () { index++ // 选出 index 小图片 做操作 // console.log(index) // if (index === lis.length) { // index = 0 // } index = index % lis.length common() }) // 需求⑤:左侧按钮播放效果 // 点击左侧按钮,可以自动播放上一张图片 // 需要一个变化量 index 不断自减 // 然后播放上一张图片 // 如果到了第一张,必须要从最后一张播放 // 教你一招: 索引号 = (数组长度 + 索引号) % 数组长度 prev.addEventListener('click', function () { index-- // 选出 index 小图片 做操作 // console.log(index) if (index < 0) { index = lis.length - 1 } // index = (lis.length + index) % lis.length common() }) // 需求⑥: // 因为左侧按钮和右侧按钮里面有大量相同的操作,可以抽取封装一个函数 common function common() { document.querySelector('.indicator .active').classList.remove('active') lis[index].classList.add('active') // 选出 index 大图片 做操作 document.querySelector('.slides ul .active').classList.remove('active') piclis[index].classList.add('active') text.innerHTML = `第${index + 1}张图的描述信息` } // 需求⑦:开启定时器 // 其实定时器自动播放,就相当于点击了右侧按钮,此时只需要, next.click() let timer = setInterval(function () { // 自动调用右侧按钮的点击事件 next.click() }, 1000) // 需求⑧: // 鼠标经过停止定时器 (清除定时器) main.addEventListener('mouseenter', function () { clearInterval(timer) }) // 鼠标离开开启定时器 (开启定时器) main.addEventListener('mouseleave', function () { timer = setInterval(function () { // 自动调用右侧按钮的点击事件 next.click() }, 1000) }) </script> </body> </html>
拓展案例:手风琴
效果:
-
Code:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>手风琴</title> <style> ul { list-style: none; } * { margin: 0; padding: 0; } div { width: 1200px; height: 400px; margin: 50px auto; border: 1px solid red; overflow: hidden; } div li { width: 240px; height: 400px; float: left; transition: all 500ms; } div ul { width: 1200px; } </style> </head> <body> <div id="box"> <ul> <li> <a href="#"> <img src="./images/1.jpg" alt=""> </a> </li> <li> <a href="#"> <img src="./images/2.jpg" alt=""> </a> </li> <li> <a href="#"> <img src="./images/3.jpg" alt=""> </a> </li> <li> <a href="#"> <img src="./images/4.jpg" alt=""> </a> </li> <li> <a href="#"> <img src="./images/5.jpg" alt=""> </a> </li> </ul> </div> </body> <script> // 获取元素 let box = document.querySelectorAll('li');// lis = [li, li, li, li, li] // 分析: // 1、鼠标进入显示图片, // 鼠标进入li,让当前li变成800,其他的li变成100 for (let i = 0; i < box.length; i++) { box[i].addEventListener('mouseenter', function () { for (let j = 0; j < box.length; j++) {// 事件触发执行,为了让所有li变成240宽的 box[j].style.width = '100px'; } this.style.width = '800px' }) box[i].addEventListener('mouseleave', function () { // 让所有的li变成240 for (let j = 0; j < box.length; j++) {// 事件触发执行,为了让所有li变成240宽的 box[j].style.width = '240px'; } }) } </script> </html>