Web APIs - 第5天
学习元素滚动、资源加载、大小、位置、动画等 DOM 知识,能够编写可交互的网页特效。
- 能够使用延时函数创建定时任务
 - 能够监听外部资源的加载状态
 - 能够监听页面元素的滚动状态
 - 能够动态获取元素的大小及位置
 - 能够结合 JavaScript 编写可交互的网页特效
 
DOM
知道如何触发焦点事件和输入事件,能够动态获取元素大小及位置,编写可交互的网页特效。
滚动事件
在页面发生滚动时触发scroll事件
页面滚动,前提时时必须有滚动条
- 页面滚动
 - 盒子内滚动,文档滚动
 
- 添加事件监听,谁有滚动条
 
<body><div class="box"><p>网页事件---滚动事件</p><p>网页事件---滚动事件</p><p>网页事件---滚动事件</p></div></body><script>//页面滚动,前提时时必须有滚动条;//盒子内滚动,文档滚动//盒子滚动,添加事件监听;谁有滚动条let box = document.querySelector('.box')box.addEventListener("scroll", function () {// this:box dom 节点属性console.log(this.scrollTop)})//页面滚动;页面有滚动条,因为body高度太高了document.addEventListener("scroll", function () {//页面卷入高度值1console.log(document.documentElement.scrollTop);})</script>
总结:
- 监听页面的滚动, 
window 和 document均可监听该事件 - 在滚动监听的过程中能实时获取页面滚动的距离
 
案例-滚动
body { height: 2000px;}<body><div class="box">澳门</div><script>let box = document.querySelector('.box')document.addEventListener("scroll", function () {let he = document.documentElement.scrollTopif (he > 200) {box.style.display = 'block'} else {box.style.display = 'none'}})//需求:当用户点击返回顶部,真的页面回到顶部box.addEventListener("click", function () {document.documentElement.scrollTop = 0})</script>
加载事件
加载事件是浏览器加载,如图片、外联JavaScript、外联 CSS等外部资源完成时触发的事件。
load:加载- 有关这个页面所有的资源加载完成后, 才执行后面函数
 所有资源:
- 外联资源css Image
 - DOM节点加载渲染完成后
 
推荐:写js代码,放在
</boyd>里边<p>这是个段落标签,测试img事件</p><img src="./images/卡车.jpg" alt=""><script>//给window,可以添加给imglet img = document.querySelector("img")//<img src="./images/卡车.jpg" alt=""> img标签 给这个位置创建img 节点// img里面是什么内容 img.src自己结局// 继续往下执行// console.log(img.width) //输出宽度的时候,图片没有加载完成0//load:加载img.addEventListener("load", function () {// 当img资源加载完成以后console.log(img.width)})//场景:涉及图片加载,为了严谨性、最好需要等待资源加载完成后,在做js操作</script>
总结:
- 外部资源加载完成时会触发 
load事件 window对象监听load事件时等待所有外部资源加载完毕后才会被触发- .将 
script 标签置于 head 标签中,不能保证成功获取 DOM 节点, - 推荐将 
script标签置于</body>之前 
元素大小
动态获取 DOM 元素的大小与位置是开发网页特效所必不可少的
- 在学习 CSS 盒模型时,我们知道影响盒子大小的因素包括:
content、padding、border,在使用JavaScript 动态获取 DOM 元素大小时同样需要从这 3 个方面考虑 
<div class="box"></div><script>let box = document.querySelector(".box")// console.log(box.style.width); //获取行内样式宽度// DOM节点: .offsetWidth / innerHeight// width/height+padding+borderconsole.log(box.offsetWidth)//DOM节点:clientWidth/height// width/height+paddingconsole.log(box.clientWidth)//DOM节点:clientLeft/Top// border边框console.log(box.clientTop)</script>
结论:
clientWidth / Height动态获取元素的大小,包含内边距。注:如有滚动条需要减掉滚动条的宽
度,chrome 滚动条宽 15pxoffsetWidth / Height动态获取元素的大小,包含内边距和边框
关于元素盒子的大小还有一种形式,那就是内容溢出产生滚动时的情形
scrollWidth / Height动态获取元素滚动区域的大小clientLeft/Top动态获取border的边框大小
案例-滚动到底部
<style>.box {width: 300px;height: 200px;border: 20px solid #999999;background-color: pink;padding: 10px 30px;overflow: auto;}p {height: 600px;padding: 10px;margin: 0;border: 1px solid #000;background-color: green;}</style><div class="box"><p>文字内容~~~</p></div><script>let box = document.querySelector('.box')let cha = box.scrollHeight - box.clientHeightbox.addEventListener("scroll", function () {if (box.scrollHeight - cha) {alert('请续费过后才能继续阅读!!!')}})</script>
元素位置
JavaScritp 有能力动态获取元素相对于父(祖先)元素的位置
- 结论:
<div class="father"><div class="son"></div></div><button class="btn">获取father的位置</button><script>//获取元素let father = document.querySelector('.father')let btn = document.querySelector('.btn')//点击按钮动态获取元素位置btn.addEventListener('click', function () {father.innerHTML = `顶部:${father.offsetTop}左侧:${father.offsetLeft}`})</script>
 
offsetLeft/Top相对最近的已定位的祖先元素的位置,当所有祖先都未定位则参照body元
素, fixed 定位的元素始终参照浏览器窗口。offsetParent获取元素位置时所参照的祖先元素JavaScript也能动态获取元素滚动的位置。
scrollLeft/Top动态获取元素滚动的位置scrollLeft/Top允许被赋值动态改变元素的滚动位置获取整个页面的滚动位置通过
documentElement元素
- 新的方法 
getBoundingClientRect获取相对于浏览器窗口的位置及元素的大小<body><h3>同时获取元素大小及位置</h3><button>获取大小及位置</button><div class="box"></div><script>// 获取 .box 元素let box = document.querySelector('.box');let btn = document.querySelector('button');// 点击获取元素的位置及大小btn.addEventListener('click', function () {let rects = box.getBoundingClientRect();console.log(rects);})</script></body
 
- 扩展:还有一对属性 
clientLeft和clientHeight实际指的是边框的宽度。 
延时函数
知道 JavaScript 中定时器函数的构成,能够基于定时器创建定的定时任务。
setTimeout
延时函数是实现程序暂缓执行的技术,如下代码所示:
<script>// 普通函数function foo() {console.log('我是反应迟钝的树獭...');}// 等待 3 秒后才会执行函数 foosetTimeout(foo, 3000);// 也可以是匿名函数setTimeout(function () {console.log('休息1秒后再执行...');}, 1000);</script>
延时函数和间歇函数统称为定时器或定时任务,间歇函数的特征是不间断的重复执行,而延时函数只会
执行 1 次。结合递归函数可以使用
setTimeout实现setInterval一样的功能!!!<script>function myInterval() {let d = new Date();document.querySelector('.clock').innerText = d.toLocaleString();// 延时任务,自调用setTimeout(myInterval, 1000);}// 启动定时任务myInterval();</script>
- 一个值得关注的细节是 
setInterval间歇函数也会延时执行,如下所示:<script>// 会延时 1秒 才会正式启动定时任务setInterval(function () {let d = new Date();document.querySelector('.clock').innerText = d.toLocaleString();}, 1000);</scrip>
 
上述执行代码的结果是等待 1 秒后,才会在网页中显示时间。
setTimeout 结合递归函数创建定时任务时,是立即执行定的,因此实践中经常使用 setTimeout 配
合递归来替代 setInterval !总结:
setInterval的特征是重复执行,首次执行会延时setTimeout的特征是延时执行,只执行 1 次setTimeout结合递归函数,能模拟 setInterval 重复执行,且不会有延时clearTimeout清除由 setTimeout 创建的定时任务
动画
JavaScript动画
setInterval 、 setTimeout 常被用于创建 JavaScript 动画。
<button>开始动画</button><div></div><script>let btn = document.querySelector("button");let div = document.querySelector("div");// 初始位置坐标btn.addEventListener("click", function () {setInterval(function () {div.style.left = div.offsetLeft + 10 + "px"}, 2);/* function fn() {div.style.left = div.offsetLeft + 1 + "px"setTimeout(fn, 1000 / 60)requestAnimationFrame(fn);}fn() */})</script>
总结;
- 创建定时任务不间断的更改元素的样式,实现动画效果
 - 借助定时器创建动画效时常使用 
setTimeout 
requestAnimationFrame是一个类似setTimeout的函数,在频繁操作 DOM 时的性能更好,但是
浏览器的兼容性较 setTimeout 稍差。<body><p class="time"></p><script>// 网页时钟function clock() {begin = Date.now();let time = document.querySelector('.time');let d = new Date;// 更新 DOMtime.innerHTML = d.toLocaleString();// 重复调用requestAnimationFrame(clock);}// 开始执行clock();</script></body>
总结:
- 频繁操作 DOM 时, 
requestAnimationFrame比setTimeout和setInterval性能更好 - 基于 JavaScript DOM 操作实现的动画多采用 
requestAnimationFrame cancelAnimationFrame停止执行requestAnimationFrame
- 频繁操作 DOM 时, 
 
综合案例-轮播图
<body><div class="box"><div class="imgs"><img src="../代码/images/01.jpg" alt=""><img src="../代码/images/02.jpg" alt=""><img src="../代码/images/03.jpg" alt=""><img src="../代码/images/04.jpg" alt=""><img src="../代码/images/05.jpg" alt=""></div><button class="pre">前一张</button><button class="next">下一张</button></div><script>// 需求:点击下一张,向左移动一格图片(w)距离 位移负数//// 推导// 点击第1次:移动1格;// 点击第2次:移动2格;// 点击第3次:移动3格;// 点击第4次:移动4格;// 点击第5次:移动5格;// 再次点击, 不让移动// 问题: 如何知道用户现在点击是第几次呢?let img = document.querySelector("img");let next = document.querySelector(".next");let imgFather = document.querySelector(".imgs");let pre = document.querySelector('.pre')let num = 0;// 左侧移动最大格子数let max = imgFather.children.length - 1;//右侧移动最小的格子数let min = imgFather.children.length + 1img.addEventListener("load", function () {// 0.获取图片宽度 如果不是img,是div背景图片,div宽度:div.clinetWidthlet w = img.width;//下一张next.addEventListener("click", function () {// 移动格子数 和 最大左侧移动数 一样,不能移!num++if (num == imgFather.children.length) {num = 0}// if (num == max) {// return; // 终止函数!// } //老样式// num++;// 2.计算应该移动多少位移 num * 图片宽度(获取需要谨慎,等待加载完成后才能获取)let res = num * w;// 3.设置移动imgFather.style.transform = `translateX(-${res}px)`;});//上一张pre.addEventListener('click', function () {// if (num == 0) {// return;// } //老样式num--if (num == -1) {num = 4}// 1.点击次数 --->挪动格子数let ret = num * w;imgFather.style.transform = `translateX(-${ret}px)`})});</script></body>
