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 () {
//页面卷入高度值1
console.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.scrollTop
if (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,可以添加给img
let 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+border
console.log(box.offsetWidth)
//DOM节点:clientWidth/height
// width/height+padding
console.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.clientHeight
box.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 秒后才会执行函数 foo
setTimeout(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;
// 更新 DOM
time.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 + 1
img.addEventListener("load", function () {
// 0.获取图片宽度 如果不是img,是div背景图片,div宽度:div.clinetWidth
let 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>