接下来介绍的都是 BOM,也就是浏览器对象模型,这里主要和浏览器窗口相关,和页面关系就不太大了。
BOM 在不同的浏览器中有很严重的兼容性问题,所以主要介绍兼容性问题不大的方法,定时器是 BOM 中最有用的东西,所以也是最应该掌握的。
定时器实际上就是两个函数
window.setTimeout(fn,time):time (毫秒) 时间后执行 fn,返回一个定时器编号window.setInterval(fn,time):每隔 tine (毫秒) 时间执行一次 fn,返回一个定时器编号
这两个函数的参数都是两个,第一个是回调函数,第二个是事件。区别就在于 setTimeout 只执行一次,而 setInterval 会不断执行。
例如我要在 10 秒钟后输出一个 123
js
window.setTimeout(function(){
console.log(123);
},10000);例如我要每隔 10 秒输出一个 123
js
window.setInterval(function(){
console.log(123);
},10000);那接下来介绍一下前面提到的定时器编号是什么东西,我们比如我使用 setTimeout 要 100 秒后打印出一个东西,但是我希望用户点击了这个按钮之后就不打印了,这个时候就需要把这个定时器取消掉,取消定时器就需要用到这个定时器编号,我们使用 clearTimeout(),传入定时器编号,就可以取消掉这个 setTimeout 设置的定时器,同样,使用 clearInterval(),传入定时器编号,可以取消掉 setInterval 设置的定时器。
定时器是异步的, 不会阻塞代码
实际上, setInterval 是可以通过 setTimeout 来实现的
js
var timer;
function mySetInterval(fn,duration){
timer = setTimeout(function(){
//some code
mySetInterval(fn,duration);
},duration);
}
function myClearInterval(timer){
clearTimeout(timer);
}以上就用 setTimeout 模拟实现了 setInterval。
注意,定时器是非常有用的东西,比如 JS 动画就得用定时器来做,我可以让一个 DIV 每隔 10 毫秒移动 1 像素,这样连贯起来实际上就是 JS 的动画了。
这两个函数非常简单,但应用场景非常广泛,所以练习才是重点
以下是一些练习
html
<!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;
list-style: none;
}
.container {
font-size: 30px;
margin-top: 50px;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
</div>
<script>
// 获取页面中class为container的元素
var container = document.querySelector(".container");
// 设置一个定时器,每秒钟执行一次
setInterval(function () {
// 创建一个Date对象,获取当前时间
var date = new Date();
// 将当前时间格式化后,设置为container元素的innerHTML
container.innerHTML = `${date.getFullYear()}年 ${(date.getMonth() + 1).toString().padStart(2, "0")}月 ${date.getDate().toString().padStart(2, "0")}日 ${date.getHours().toString().padStart(2, "0")}时 ${date.getMinutes().toString().padStart(2, "0")}分 ${date.getSeconds().toString().padStart(2, "0")}秒`;
}, 1000);
</script>
</body>
</html>html
<!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>
body {
text-align: center;
}
p {
font-size: 2em;
}
</style>
</head>
<body>
<p class="countDown">
倒计时:<span>10</span>
</p>
<p class="clickCount">
点击数:<span>0</span>
</p>
<p class="buttons">
<button>点击</button>
<button>重置</button>
</p>
<script>
// 查询并获取页面中计数器和点击次数显示的元素
var countDown = document.querySelector('.countDown span');
var clickCount = document.querySelector('.clickCount span');
// 查询并获取所有按钮元素
var buttons = document.querySelectorAll('.buttons button');
// 获取开始按钮
var clickBtn = buttons[0];
// 获取重置按钮
var resetBtn = buttons[1];
// 标记是否开始计时
var isStart = false;
// 记录点击次数
var clickNum = 0;
// 计时器初始值
var countDownNum = 10;
// 定时器变量
var timer;
/**
* 重置游戏状态
* 清除定时器,重置标记,点击次数和计时器数值,
* 更新页面显示
*/
function reset() {
if (timer) {
clearInterval(timer);
timer = null;
}
isStart = false;
clickNum = 0;
countDownNum = 10;
countDown.innerHTML = countDownNum;
clickCount.innerHTML = clickNum;
}
// 绑定开始按钮点击事件
clickBtn.addEventListener("click", function () {
if (!isStart) {
isStart = true;
// 设置定时器,每秒减少计时器数值,直到归零
timer = setInterval(function () {
countDown.innerHTML = --countDownNum;
if (countDownNum == 0) {
clearInterval(timer);
timer = null;
alert("您本次点击了" + clickNum + "次");
reset();
}
}, 1000);
} else {
// 如果已经启动,每次点击增加点击次数
clickCount.innerHTML = ++clickNum;
}
});
// 绑定重置按钮点击事件,调用reset函数
resetBtn.addEventListener("click", reset);
</script>
</body>
</html>html
<!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;
}
.box {
width: 100px;
height: 100px;
position: absolute;
border-radius: 50%;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
// 初始化配置对象,包括大小、颜色和起始位置
var config = {
size: 100, // 定义盒模型的大小
color: "red", // 定义盒模型的颜色
initPosition: { // 定义盒模型的初始位置
x: 0,
y: 0,
}
}
var incX = 10; // 定义盒模型在x轴上每次移动的距离
var incY = 10; // 定义盒模型在y轴上每次移动的距离
var box = document.querySelector(".box"); // 获取类名为box的元素
var currentLeft = config.initPosition.x; // 当前盒模型左边的位置
var currentTop = config.initPosition.y; // 当前盒模型上边的位置
/**
* 初始化函数,用于将盒模型放置在初始位置,并检查是否超出屏幕范围
* 无参数
* 无返回值
*/
function init() {
// 检查盒模型是否超出屏幕范围
if (config.initPosition.x + config.size > document.documentElement.clientWidth || config.initPosition.y + config.size > document.documentElement.clientHeight) {
alert("超出屏幕范围");
box.style.display = "none"; // 如果超出屏幕范围,则将盒模型隐藏
return false;
}
// 设置盒模型的初始位置和背景颜色
box.style.left = config.initPosition.x + "px";
box.style.top = config.initPosition.y + "px";
box.style.backgroundColor = config.color;
}
init(); // 调用初始化函数
// 设置定时器,每隔30毫秒移动一次盒模型
setInterval(function () {
currentLeft += incX; // 在x轴上移动
currentTop += incY; // 在y轴上移动
// 当盒模型移动到边界时,改变移动方向
if (currentLeft <= 0) {
currentLeft = 0;
incX = -incX;
}
if (currentTop <= 0) {
currentTop = 0;
incY = -incY;
}
if (currentLeft + config.size >= document.documentElement.clientWidth) {
currentLeft = document.documentElement.clientWidth - config.size;
incX = -incX;
}
if (currentTop + config.size >= document.documentElement.clientHeight) {
currentTop = document.documentElement.clientHeight - config.size;
incY = -incY;
}
// 更新盒模型的位置
box.style.left = currentLeft + "px";
box.style.top = currentTop + "px";
}, 30);
</script>
</body>
</html>html
<!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;
}
.container {
/* 定义主容器类样式 */
position: relative;
/* 设置相对定位,以便内部绝对定位元素参照此容器 */
overflow: hidden;
/* 隐藏溢出内容,便于实现轮播效果 */
}
.container a {
/* 容器内的链接样式 */
display: block;
/* 将链接作为块级元素展示 */
width: 500px;
/* 设定链接宽为500px */
}
.container img {
/* 容器内的图片样式 */
display: block;
/* 将图片作为块级元素展示 */
}
.container .left,
/* 定义左右控制按钮样式 */
.container .right {
position: absolute;
/* 绝对定位,脱离正常文档流 */
top: 50%;
/* 顶部居中对齐 */
transform: translateY(-50%);
/* 使用CSS3变换垂直居中 */
background-color: rgba(0, 0, 0, .5);
/* 半透明黑色背景 */
color: white;
/* 文字颜色为白色 */
height: 50px;
/* 控制按钮高度为50px */
width: 30px;
/* 控制按钮宽度为30px */
line-height: 50px;
/* 文字行高与高度一致,居中显示文字 */
text-align: center;
/* 文本居中对齐 */
user-select: none;
/* 禁止用户选择文本 */
font-size: 25px;
/* 字体大小为25px */
cursor: pointer;
/* 更改鼠标指针为手形,表明可点击 */
}
.container .left:hover,
/* 左右按钮悬停时的样式 */
.container .right:hover {
background-color: rgba(0, 0, 0, .8);
/* 提高背景不透明度 */
}
.container .left {
/* 左侧控制按钮的位置 */
left: 0;
}
.container .right {
/* 右侧控制按钮的位置 */
right: 0;
}
.container .lists {
/* 幻灯片导航点列表样式 */
list-style: none;
/* 移除列表项目符号 */
display: flex;
/* 使用弹性布局使导航点水平居中 */
position: absolute;
/* 绝对定位 */
cursor: pointer;
/* 更改鼠标指针为手形 */
bottom: 20px;
/* 位于底部,距离底部20px */
left: 50%;
/* 居中对齐,相对于父容器左边缘 */
transform: translateX(-50%);
/* 使用CSS3变换使其水平居中 */
}
.container .lists li {
/* 导航点单个圆点样式 */
height: 15px;
/* 圆点高度为15px */
width: 15px;
/* 圆点宽度为15px */
background-color: #ccc;
/* 默认背景色为灰色 */
margin-left: 5px;
/* 相邻圆点间有5px间距 */
border-radius: 50%;
/* 圆角为半径,形成圆形 */
}
.container .lists li:hover,
/* 导航点圆点悬停时的样式 */
.container .lists li.active {
/* 当前活动幻灯片对应的圆点样式 */
background-color: #fff;
/* 背景色更改为白色 */
}
.container .lists li:first-of-type {
/* 第一个圆点取消左边距 */
margin-left: 0;
}
.container .banners {
/* 存放幻灯片图片的ul样式 */
list-style: none;
/* 移除列表项目符号 */
position: absolute;
/* 绝对定位,方便进行动画处理 */
}
.container .banners li {
/* 幻灯片图片项的样式 */
float: left;
/* 浮动布局,使图片水平排列 */
}
</style>
</head>
<body>
<div class="container">
<ul class="banners">
</ul>
<div class="left"><</div>
<div class="right">></div>
<ul class="lists">
</ul>
</div>
<script>
// 定义一个配置对象,存储幻灯片组件的相关元素和属性
var config = {
// 获取页面上class为".container"的元素,用作幻灯片容器
container: document.querySelector(".container"),
// 获取class为".banners"的元素,用作包含幻灯片图片的ul元素
banners: document.querySelector(".banners"),
// 获取class为".lists"的元素,用作幻灯片分页指示器ul
lists: document.querySelector(".lists"),
// 获取class为".left"的元素,用作左侧翻页按钮
left: document.querySelector(".left"),
// 获取class为".right"的元素,用作右侧翻页按钮
right: document.querySelector(".right"),
// 设置幻灯片容器的宽度
sizeX: 500,
// 设置幻灯片容器的高度
sizeY: 300,
// 存储幻灯片图片资源地址的数组
imgs: [
"imgs/banner1.jpg",
"imgs/banner2.jpg",
"imgs/banner3.jpg",
"imgs/banner4.jpg"
],
// 存储幻灯片链接资源的数组,这里都为"javascript:void(0)"表示无实际跳转动作
srcs: [
"javascript:void(0)",
"javascript:void(0)",
"javascript:void(0)",
"javascript:void(0)"
],
// 设置默认显示的幻灯片索引
defaultIndex: 0,
};
// 全局变量,记录当前显示的幻灯片索引,初始值为配置对象中的defaultIndex
var currentIndex = config.defaultIndex;
// 设置幻灯片容器尺寸
config.container.style.width = config.sizeX + "px";
config.container.style.height = config.sizeY + "px";
// 设置幻灯片ul元素的总宽度,并根据当前索引设置其left偏移量
config.banners.style.width = ((config.imgs.length + 1) * 100) + "%";
config.banners.style.left = (-currentIndex * config.sizeX) + "px";
// 遍历图片资源数组,为幻灯片和分页指示器添加相应内容
config.imgs.forEach(function (item, i) {
// 为每个图片创建<li>元素,包括链接和图片源,并将其添加到幻灯片ul中
config.banners.innerHTML += `<li><a href="${config.srcs[i]}"><img src="${item}" alt=""></a></li>`;
// 为分页指示器添加对应的<li>元素
config.lists.innerHTML += `<li></li>`;
});
// 在幻灯片ul末尾追加第一个图片元素,以便实现无缝轮播
config.banners.innerHTML += `<li><a href="${config.srcs[0]}"><img src="${config.imgs[0]}" alt=""></a></li>`;
// 将当前默认显示的幻灯片对应的分页指示器项设置为激活状态
config.lists.children[currentIndex].classList.add("active");
// 定义函数,用于跳转到指定的幻灯片页数
function toIndex(i) {
// 如果当前索引与目标索引相同,无需执行任何操作
if (currentIndex === i) return;
// 如果目标索引不在有效范围内,也无需执行操作
if (i < 0 || i > config.imgs.length) return;
// 更新当前显示的幻灯片索引
currentIndex = i;
// 计算目标幻灯片应有的left偏移量
var left = -i * config.sizeX;
// 获取当前幻灯片的left偏移量,并转换为浮点数
var currentLeft = Number.parseFloat(config.banners.style.left);
// 计算需要移动的距离
var dis = Math.abs(left - currentLeft);
// 确定移动方向(正负1)
var flag = currentLeft > left ? -1 : 1;
// 移除当前激活的分页指示器项的激活样式
config.lists.querySelector(".active").classList.remove("active");
// 为新的目标幻灯片页数所对应的分页指示器项添加激活样式
if (config.lists.children[i]) {
config.lists.children[i].classList.add("active");
} else {
// 若目标索引超出了范围,则激活第一个指示器项
config.lists.children[0].classList.add("active");
}
// 使用定时器平滑移动幻灯片
var timer;
// 根据需要移动的距离调整动画速度,使得在500毫秒内完成切换,每次移动5像素
timer = setInterval(function () {
currentLeft = currentLeft + flag * 5;
// 判断是否已到达目标位置
if ((flag == -1 && currentLeft < left) || (flag == 1 && currentLeft > left)) {
// 到达目标位置后清除定时器,并将left值设定为目标值
currentLeft = left;
clearInterval(timer);
// 处理循环边界条件:如果滚动到最后一张图片后,回到第一张
if (i == config.imgs.length) {
currentLeft = 0;
currentIndex = 0;
}
config.banners.style.left = currentLeft + 'px';
}
// 实时更新幻灯片的left偏移量
config.banners.style.left = currentLeft + 'px';
}, 2500 / dis);
}
// 为分页指示器添加点击事件监听器,当点击某个指示器项时跳转到对应幻灯片
config.lists.addEventListener("click", function (e) {
// 点击的目标如果不是分页指示器项,则返回
if (e.target.nodeName !== "LI") return;
// 根据点击的指示器项索引调用toIndex函数
toIndex(Array.from(config.lists.children).indexOf(e.target));
});
// 为右侧翻页按钮添加点击事件监听器,点击后跳转到下一张幻灯片
config.right.addEventListener("click", function (e) {
toIndex(currentIndex + 1);
});
// 为左侧翻页按钮添加点击事件监听器,点击后跳转到上一张幻灯片,考虑循环播放情况
config.left.addEventListener("click", function (e) {
// 当前索引为0时,先滚动到最后一张,然后调用toIndex函数切换至最后一张
if (currentIndex == 0) {
config.banners.style.left = -(config.imgs.length) * config.sizeX + "px";
toIndex(config.imgs.length - 1);
} else {
// 否则直接调用toIndex函数切换至上一张
toIndex(currentIndex - 1);
}
});
</script>
</body>
</html>