主题
2-2样式操作及案例(二)
本页对应 第二章WebAPIs,共 5 段视频,约 55 分钟。
学习目标
- 能使用
classList.add()、classList.remove()、classList.toggle()对元素类名进行追加、删除和切换,并验证不影响原有类名。 - 能基于对象数组和随机索引,更新轮播图图片
src、文字内容及背景颜色。 - 能使用
:nth-child()选择对应小圆点,并通过classList.add('active')实现高亮显示。 - 能读写表单元素的
type、value属性,并使用disabled、checked布尔属性控制按钮禁用与复选框勾选状态。 - 能定义
data-*自定义属性,并通过dataset读取其中存储的数据。
知识点
先明确:间隔函数解决什么问题
为什么需要: 网页里有些功能不需要用户手动触发,而是“每隔一段时间自动做一次”。笔记里举的例子是倒计时:时间要一秒一秒地减少,不能靠用户每点一次按钮才更新。
怎么想: 这类需求的关键词是“重复”和“固定间隔”。JavaScript 把这类能力做成定时器函数,笔记说明定时器有两种:间隔函数和延迟函数,本节先学间隔函数 setInterval。
怎么做: 记住 setInterval 的作用:间隔固定的时间,自动重复执行另一个函数,也叫定时器函数。
易错点: “间隔函数”不是执行一次就结束;它会在没有关闭前反复执行。笔记里的“间隔时间单位是毫秒”,不要按秒直接写 1,要写 1000 才表示 1 秒。
开启定时器:setInterval 的写法
为什么需要: 只有知道开启语法,才能把“每隔一段时间自动执行”落到代码里。
怎么想: setInterval 接收两个核心信息:要重复执行的函数,以及间隔多少毫秒。笔记的示例是每秒打印一次。
怎么做: 保留笔记代码:
html
<script>
setInterval(function () {
console.log('我是1秒钟执行一次')
}, 1000)
</script>这段代码的意思是:每隔 1000 毫秒调用一次这个匿名函数。
易错点: 第一,单位是毫秒,1000 才是 1 秒;第二,第一个参数必须是函数或函数名,不是函数执行结果;第三,如果写成 repeat(),那是立刻调用并把返回值传进去,不是把函数交给定时器。
关闭定时器:clearInterval 与定时器编号
为什么需要: 间隔函数会一直重复,很多场景不能让它无限跑,比如倒计时结束、页面切换、组件销毁时都要停掉。
怎么想: 要关闭某个定时器,先得“记住”它。setInterval 的返回值是一个唯一的数字,可以存到变量里,再用 clearInterval 关闭。笔记强调:需要定时器变量名来关闭,返回的是一个唯一的数字。
怎么做: 保留笔记代码:
html
<script>
let timer = setInterval(repeat, 1000) // 注意调用的时候直接写函数名字不需要写小括号
console.log(timer) // 1
let timer1 = setInterval(repeat, 1000) // 注意调用的时候直接写函数名字不需要写小括号
console.log(timer1) // 2
// 2. 关闭定时器
clearInterval(timer)
</script>这里 timer 和 timer1 分别保存两个定时器的编号;clearInterval(timer) 只关闭 timer 对应的那个。
易错点: 笔记中的 repeat 是示意函数名,实际使用时要先定义它;传函数名时不要加小括号。还要注意:clearInterval 里放的是定时器编号,不是函数名。
调试与验证:把“看不见的重复”变成可观察
为什么需要: 定时器是异步重复执行,光看代码不容易确认它有没有开启、编号是多少、关闭后有没有停。
怎么想: 转写稿里反复用“打印一下”“打开控制台”“Ctrl+S 保存”“刷新看效果”来验证。转写稿中的同音字如“卡加斯保存”“走着”“控制台”,应纠正为 Ctrl+S 保存、运行/刷新页面、打开控制台。转写稿没有直接讲 setInterval 的调试,但这种验证顺序可以迁移:先打印 timer 看返回值,再观察重复输出,最后调用 clearInterval 看是否停止。
怎么做: 可以先写 console.log(timer) 确认拿到唯一数字;再让回调里 console.log 输出内容,观察是否按间隔重复;关闭后再看输出是否停止。
易错点: 不要只看一次输出就下结论;刷新页面会重新开始定时器,观察时要分清“页面刷新”与“定时器重复”。
易错点汇总
- 间隔时间单位是毫秒,
1000才是 1 秒。 setInterval(repeat, 1000)中传函数名不加小括号;写成repeat()会立刻执行。- 关闭定时器要用
setInterval返回的变量,不要重新写一个函数名去关。 - 多个定时器会返回不同数字,关闭哪一个就用哪一个变量。
- 定时器不会自动停,不需要时必须
clearInterval。
案例目标与页面结构
为什么需要:静态轮播图只能手动切换,实际页面更常见“定时自动播放”。本案例要把图片、标题文字、底部背景色和指示小圆点一起联动更新。
怎么想:页面结构分两大块:.slider-wrapper 放图片,.slider-footer 放文字、小圆点和左右按钮。数据不写死在 DOM 里,而是准备一个对象数组,每个对象包含 url、title、color,切换时按索引取对象。
怎么做:先保留笔记中的完整结构,图片 alt 改为中文说明,再在脚本中准备 sliderData。
易错点:图片路径要写对;.slider-wrapper img 和 .slider-footer p 要能准确选中;不要把小圆点的 li 与图片数组顺序弄混。
准备数据与获取元素
为什么需要:轮播图每次切换要同时改四处内容,如果分别写四套数据,维护困难。用对象数组可以把“一张图对应的信息”打包在一起。
怎么想:数组每一项就是一个“幻灯片对象”。索引 i 指向当前显示的对象,取 sliderData[i].url、.title、.color 即可。
怎么做:
js
const sliderData = [
{ url: './images/slider01.jpg', title: '对人类来说会不会太超前了?', color: 'rgb(100, 67, 68)' },
// ...其余数据保留
]
const img = document.querySelector('.slider-wrapper img')
const p = document.querySelector('.slider-footer p')
const footer = document.querySelector('.slider-footer')易错点:querySelector 只取第一个匹配元素;数组长度就是幻灯片数量,不要手写死 8。
用信号量和定时器实现自动切换
为什么需要:自动轮播需要“每隔一段时间换下一张”。定时器负责节奏,信号量 i 负责当前索引。
怎么想:声明 let i = 0,每次定时器执行先 i++,再用新索引取对象。转写稿中提到的“信号量”就是这里的索引变量。
怎么做:
js
let i = 0
setInterval(function () {
i++
i = i % sliderData.length
console.log(i)
img.src = sliderData[i].url
p.innerText = sliderData[i].title
footer.style.backgroundColor = sliderData[i].color
}, 1000)易错点:i 要用 let,否则不能重新赋值;调试时先 console.log(i) 看索引是否按 0、1、2... 循环;innerText 用于纯文字标题。
取余实现无缝衔接
为什么需要:播放到最后一张后,如果继续 i++ 会得到 8,而数组最大索引是 7,会取到 undefined。
怎么想:可以用 if (i >= sliderData.length) { i = 0 },也可以写成三元运算符,笔记最终采用取余:i = i % sliderData.length。当 i 等于数组长度时,余数回到 0。
怎么做:在 i++ 后立即执行 i = i % sliderData.length,再更新页面。
易错点:取余要放在“取数据之前”;不要写成 i % 8 写死长度,数组增减后会出错。
同步更新小圆点 active
为什么需要:只换图片不换小圆点,用户看不出当前是第几张。小圆点高亮需要和当前索引同步。
怎么想:当前索引 i 从 0 开始,而 CSS 的 :nth-child() 从 1 开始,所以第 i 张对应第 i + 1 个 li。先用 classList.remove('active') 移除旧高亮,再给新 li 添加 active。
怎么做:
js
document.querySelector('.slider-indicator .active').classList.remove('active')
document.querySelector(`.slider-indicator li:nth-child(${i + 1})`).classList.add('active')易错点:顺序不能颠倒,笔记强调先移除再添加;nth-child 里的变量要用模板字符串;classList.add/remove 是方法,后面必须加小括号。转写稿中反复强调 classList 不会覆盖已有类名,这里正适合切换 active。
完整代码
js
<!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>综合案例-定时版轮播图</title>
<style>
* { box-sizing: border-box; }
.slider { width: 560px; height: 400px; overflow: hidden; }
.slider-wrapper { width: 100%; height: 320px; }
.slider-wrapper img { width: 100%; height: 100%; display: block; }
.slider-footer {
height: 80px; background-color: rgb(100, 67, 68);
padding: 12px 12px 0 12px; position: relative;
}
.slider-footer .toggle { position: absolute; right: 0; top: 12px; display: flex; }
.slider-footer .toggle button {
margin-right: 12px; width: 28px; height: 28px; appearance: none;
border: none; background: rgba(255, 255, 255, 0.1); color: #fff;
border-radius: 4px; cursor: pointer;
}
.slider-footer .toggle button:hover { background: rgba(255, 255, 255, 0.2); }
.slider-footer p { margin: 0; color: #fff; font-size: 18px; margin-bottom: 10px; }
.slider-indicator {
margin: 0; padding: 0; list-style: none; display: flex; align-items: center;
}
.slider-indicator li {
width: 8px; height: 8px; margin: 4px; border-radius: 50%;
background: #fff; opacity: 0.4; cursor: pointer;
}
.slider-indicator li.active { width: 12px; height: 12px; opacity: 1; }
</style>
</head>
<body>
<div class="slider">
<div class="slider-wrapper">
<img src="./images/slider01.jpg" alt="轮播图图片" />
</div>
<div class="slider-footer">
<p>对人类来说会不会太超前了?</p>
<ul class="slider-indicator">
<li class="active"></li><li></li><li></li><li></li>
<li></li><li></li><li></li><li></li>
</ul>
<div class="toggle">
<button class="prev"><</button>
<button class="next">></button>
</div>
</div>
</div>
<script>
const sliderData = [
{ url: './images/slider01.jpg', title: '对人类来说会不会太超前了?', color: 'rgb(100, 67, 68)' },
{ url: './images/slider02.jpg', title: '开启剑与雪的黑暗传说!', color: 'rgb(43, 35, 26)' },
{ url: './images/slider03.jpg', title: '真正的jo厨出现了!', color: 'rgb(36, 31, 33)' },
{ url: './images/slider04.jpg', title: '李玉刚:让世界通过B站看到东方大国文化', color: 'rgb(139, 98, 66)' },
{ url: './images/slider05.jpg', title: '快来分享你的寒假日常吧~', color: 'rgb(67, 90, 92)' },
{ url: './images/slider06.jpg', title: '哔哩哔哩小年YEAH', color: 'rgb(166, 131, 143)' },
{ url: './images/slider07.jpg', title: '一站式解决你的电脑配置问题!!!', color: 'rgb(53, 29, 25)' },
{ url: './images/slider08.jpg', title: '谁不想和小猫咪贴贴呢!', color: 'rgb(99, 72, 114)' },
]
const img = document.querySelector('.slider-wrapper img')
const p = document.querySelector('.slider-footer p')
const footer = document.querySelector('.slider-footer')
let i = 0
setInterval(function () {
i++
i = i % sliderData.length
console.log(i)
img.src = sliderData[i].url
p.innerText = sliderData[i].title
footer.style.backgroundColor = sliderData[i].color
document.querySelector('.slider-indicator .active').classList.remove('active')
document.querySelector(`.slider-indicator li:nth-child(${i + 1})`).classList.add('active')
}, 1000)
</script>
</body>
</html>课件图示








代码示例
示例 1:间隔函数 setInterval 的开启与关闭
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>间隔函数 setInterval</title>
</head>
<body>
<script>
// 1. 开启定时器:每隔 1000 毫秒(1 秒)自动执行一次函数
let n = 0
let timer = setInterval(function () {
n++
console.log('我是1秒钟执行一次', n)
// 执行 5 次后自己把自己关掉
if (n >= 5) {
clearInterval(timer)
}
}, 1000)
// 2. 具名函数写法:调用时直接写函数名字,不要写小括号
function repeat() {
console.log('repeat 被调用了')
}
let timer1 = setInterval(repeat, 1000)
// 定时器返回的是一个唯一的数字
console.log(timer) // 1
console.log(timer1) // 2
// 3. 关闭定时器:需要定时器变量名
clearInterval(timer1)
</script>
</body>
</html>要点解释
setInterval(函数, 间隔时间)中,间隔时间的单位是毫秒,1000就是 1 秒;不写时间间隔或写错单位,会导致“看起来没执行”或“执行太快”。setInterval(repeat, 1000)里传的是函数名,不能写成repeat()。写成repeat()是立即调用函数并把返回值传进去,定时器就失效了。setInterval的返回值是一个唯一的数字(本例依次是 1、2),关闭定时器必须靠这个数字:clearInterval(timer)。- 易错点:
clearInterval里要放定时器变量名,不是放函数名;setInterval只执行一次就不再执行,通常是没写间隔时间或函数名写错。 setInterval不会自动停止,不用了要手动clearInterval,否则会一直执行。
示例 2:综合案例——轮播图定时版(图片路径需自备 images 文件夹)
js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>综合案例-定时版轮播图</title>
<style>
* {
box-sizing: border-box;
}
.slider {
width: 560px;
height: 400px;
overflow: hidden;
}
.slider-wrapper {
width: 100%;
height: 320px;
}
.slider-wrapper img {
width: 100%;
height: 100%;
display: block;
}
.slider-footer {
height: 80px;
background-color: rgb(100, 67, 68);
padding: 12px 12px 0 12px;
position: relative;
}
.slider-footer .toggle {
position: absolute;
right: 0;
top: 12px;
display: flex;
}
.slider-footer .toggle button {
margin-right: 12px;
width: 28px;
height: 28px;
appearance: none;
border: none;
background: rgba(255, 255, 255, 0.1);
color: #fff;
border-radius: 4px;
cursor: pointer;
}
.slider-footer .toggle button:hover {
background: rgba(255, 255, 255, 0.2);
}
.slider-footer p {
margin: 0;
color: #fff;
font-size: 18px;
margin-bottom: 10px;
}
.slider-indicator {
margin: 0;
padding: 0;
list-style: none;
display: flex;
align-items: center;
}
.slider-indicator li {
width: 8px;
height: 8px;
margin: 4px;
border-radius: 50%;
background: #fff;
opacity: 0.4;
cursor: pointer;
}
.slider-indicator li.active {
width: 12px;
height: 12px;
opacity: 1;
}
</style>
</head>
<body>
<div class="slider">
<div class="slider-wrapper">
<img src="./images/slider01.jpg" alt="" />
</div>
<div class="slider-footer">
<p>对人类来说会不会太超前了?</p>
<ul class="slider-indicator">
<li class="active"></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<div class="toggle">
<button class="prev"><</button>
<button class="next">></button>
</div>
</div>
</div>
<script>
// 初始数据
const sliderData = [
{ url: './images/slider01.jpg', title: '对人类来说会不会太超前了?', color: 'rgb(100, 67, 68)' },
{ url: './images/slider02.jpg', title: '开启剑与雪的黑暗传说!', color: 'rgb(43, 35, 26)' },
{ url: './images/slider03.jpg', title: '真正的jo厨出现了!', color: 'rgb(36, 31, 33)' },
{ url: './images/slider04.jpg', title: '李玉刚:让世界通过B站看到东方大国文化', color: 'rgb(139, 98, 66)' },
{ url: './images/slider05.jpg', title: '快来分享你的寒假日常吧~', color: 'rgb(67, 90, 92)' },
{ url: './images/slider06.jpg', title: '哔哩哔哩小年YEAH', color: 'rgb(166, 131, 143)' },
{ url: './images/slider07.jpg', title: '一站式解决你的电脑配置问题!!!', color: 'rgb(53, 29, 25)' },
{ url: './images/slider08.jpg', title: '谁不想和小猫咪贴贴呢!', color: 'rgb(99, 72, 114)' },
]
// 获取元素
const img = document.querySelector('.slider-wrapper img')
const p = document.querySelector('.slider-footer p')
const footer = document.querySelector('.slider-footer')
// 1. 声明1个变量,利用定时器实现变量++,依次拿到下一个数组中的对象
// 1.1 声明变量 i
let i = 0 // 信号量
// 1.2 开启定时器实现 i++
setInterval(function () {
i++
// 无缝衔接 如果播放到了最后一张 第8张,则快速复原为第1张播放
// 其实这个8就是数组的长度
// if (i >= sliderData.length) {
// i = 0
// }
// 改为三元运算符的写法
// i = i >= sliderData.length ? 0 : i
// 利用取余来实现
i = i % sliderData.length
console.log(i)
// 1.3 依次拿到下一个数组中的对象
// console.log(sliderData[i])
// 2. 利用对象更换图片、文字、背景颜色、小圆点
// 2.1 更换图片
img.src = sliderData[i].url
// 2.2 更换文字内容
p.innerText = sliderData[i].title
// 2.3 更换背景颜色
footer.style.backgroundColor = sliderData[i].color
// 2.4 更换小圆点
// 先把以前的 active 类 移除掉 顺序不能颠倒
document.querySelector('.slider-indicator .active').classList.remove('active')
// 添加小圆点
document.querySelector(`.slider-indicator li:nth-child(${i + 1})`).classList.add('active')
}, 1000)
</script>
</body>
</html>要点解释
- 核心思路:用
setInterval让信号量i每秒自增,再用i去数组sliderData里取对象,一个对象同时提供url、title、color三份数据,一次替换三处内容。 i = i % sliderData.length是实现无缝衔接的关键行:走到第 8 张后8 % 8 === 0,自动回到第 1 张。易错点是把i >= length的判断写在i++之后却忘了复位,导致sliderData[i]越界拿到undefined,图片变成空白。- 小圆点要用
`li:nth-child(\${i + 1})`,因为i是从 0 开始的索引,而nth-child从 1 开始计数,少加 1 就会整体错位一格。 - 易错点:
classList.remove('active')必须写在classList.add('active')之前,顺序颠倒会出现两个小圆点同时高亮;并且选择器.slider-indicator .active在页面上必须真的存在一个active元素,否则document.querySelector(...)返回null,再调.classList会报错。 - 换背景色用
footer.style.backgroundColor,属性名是驼峰写法,不能写成style.background-color。 - 案例依赖
./images/slider01.jpg ~ slider08.jpg这 8 张图片,缺少图片时页面只有文字和小圆点在切换。
示例 3:classList 追加 / 删除 / 切换类名
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>classList 操作元素样式</title>
<style>
.box {
width: 200px;
height: 200px;
background-color: pink;
}
.circle {
border-radius: 50%;
}
</style>
</head>
<body>
<div class="box">box</div>
<script>
const box = document.querySelector('.box')
// 1. 追加类名:不影响以前的类名
box.classList.add('circle')
// 2. 删除类名:只删除指定的类名,其他类名不受影响
// box.classList.remove('box')
// 3. 切换类名:原来有就删掉,原来没有就加上
// box.classList.toggle('circle')
</script>
</body>
</html>要点解释
classList的三个方法add、remove、toggle都是方法,后面一定要加小括号;写成box.classList.add而不调用,什么效果都不会发生。- 参数是字符串形式的类名,类名前面不加点,写
add('circle')而不是add('.circle')。 classList.add是追加,不会覆盖元素原有的box类名——这正是它比className好用的地方,className = 'circle'会把原来的类名整个覆盖掉。classList.toggle是“有则删、无则加”,做开关效果(如示例 2 里小圆点高亮切换)时非常方便。- 修改的样式很少时用
style;样式较多、已在 CSS 中定义好类时,用classList添加类名。
常见错误
定时器一开就"疯跑",页面卡死
- 现象:
setInterval(repeat(), 1000)里函数只执行了一次,或控制台瞬间刷屏。 - 原因:
repeat()带小括号是立即调用,把返回值(undefined)交给了setInterval。 - 修法:传函数名本身
setInterval(repeat, 1000),或用setInterval(function () { ... }, 1000)包装。
- 现象:
轮播到最后一张就报
Cannot read properties of undefined- 现象:能正常播 8 张,第 9 秒报错并卡住。
- 原因:信号量
i只做i++,越过了数组最大索引。 - 修法:每次自增后立即收敛,
i = i % sliderData.length(或i = i >= arr.length ? 0 : i),且这一行必须写在"取数据"之前。
关不掉的定时器
- 现象:调了
clearInterval(timer)但动画还在跑;或点击"暂停"毫无反应。 - 原因:① 关闭时传的是函数名而不是 id;② 定时器 id 存在函数内部的局部变量里,外部拿不到;③ 每次开启都重新
let timer,旧 id 被覆盖。 - 修法:
let timer = null声明在最外层作用域,开启时timer = setInterval(...),关闭时clearInterval(timer)。
- 现象:调了
点两次"开始"后速度翻倍
- 现象:反复点开始按钮,轮播越来越快,甚至出现残影。
- 原因:每点一次就新建一个定时器,多个定时器同时在跑。
- 修法:开启前先
clearInterval(timer)(先清后开),保证同一时刻只有一个定时器存活。
小圆点出现"两个高亮"或直接报 null
- 现象:切换后同时亮两个点;或首次加载控制台报
Cannot read properties of null (reading 'classList')。 - 原因:① 先
add后remove,或只加不减;② 页面初始 HTML 里没有.active,querySelector('.active')返回null。 - 修法:严格"先移除旧的、再添加新的";并保证 HTML 中第一个圆点默认带
active,或移除前加空值判断。
- 现象:切换后同时亮两个点;或首次加载控制台报
用
className加高亮,把原类名冲掉了- 现象:圆点加了
active后原有样式全没了。 - 原因:
className是整体赋值,会覆盖原有类名。 - 修法:改用
classList.add('active')/remove/toggle,注意这三个是方法,必须带小括号。
- 现象:圆点加了
复选框/按钮的"开关"失效
- 现象:写了
btn.disabled = 'false',按钮仍然是禁用状态。 - 原因:布尔属性赋了字符串,非空字符串在判断时被视为真。
- 修法:只传布尔值
true/false,不加引号。
- 现象:写了
读取用户输入拿到空值或
undefined- 现象:想拿到输入框里用户打的字,结果打印出空字符串或
undefined。 - 原因:用了
innerText/innerHTML,或选择器[name="username"]的值与 HTML 中name不一致。 - 修法:表单取值统一用
.value;选择器先单独打印元素确认选对了再取属性。
- 现象:想拿到输入框里用户打的字,结果打印出空字符串或
dataset取不到值- 现象:
data-my-name="box",写box.dataset['my-name']或box.dataset.my-name都是undefined或报错。 - 原因:
data-后面的连字符命名会被转成驼峰。 - 修法:
box.dataset.myName。
- 现象:
练习
可控倒计时:页面上放"开始 / 暂停 / 重置"三个按钮和一个显示秒数的
div。要求:开始后每秒 +1;暂停后数字定格;重置回 0。反复快速点"开始"不能出现加速。(提示:先想清楚timer变量应该声明在哪一层。)轮播图加暂停键:在综合案例的轮播图上,把底部
.toggle的两个按钮改造成"暂停 / 播放"功能,暂停时当前图片、文字、背景色、小圆点全部定格,恢复播放时从定格的那一张继续。随机轮播图加手动切换:在随机版轮播图的基础上,保留刷新随机显示,同时用
setInterval每 2 秒自动切下一张,并让左右箭头能手动上一张 / 下一张。要求手动点击后小圆点、图片、文字、背景色四者永远同步。密码显隐:输入框
type默认为password,旁边一个"眼睛"按钮,点击在password/text之间切换,按钮文字同步在"👁 / 🙈"之间切换;再要求用disabled控制:密码框为空时按钮不可点。待办列表删除:用循环渲染 4 条待办,每条后面跟一个"删除"链接,用
data-index存下标。点击任意一条只删掉它自己,其余不动。(提示:渲染时给每个链接写上data-index="\${i}",事件里用dataset.index拿到下标。)
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 通过classList操作元素样式 | 07:02 |
| 2 | 随机轮播图案例-更换图片 | 12:23 |
| 3 | 随机轮播图案例-更换文字颜色和小圆点 | 13:25 |
| 4 | 操作表单常见属性 | 14:47 |
| 5 | 自定义属性 | 07:46 |
小结
- 定时器的一切坑,本质都是**"谁持有 id、谁负责清"**没想清楚——先定作用域,再写代码。
setInterval的回调里只做"取数据 → 渲染"两件事,把索引收敛(取余)放在渲染之前,是轮播类需求的固定套路。- 涉及"索引 ↔ 页面元素"的对应关系时,
nth-child(i + 1)的+1不是技巧,而是"索引从 0 开始、孩子从 1 开始"这一事实的必然结果。 - 状态切换类操作(高亮、显隐、勾选)遵循"先清后设":旧状态先移除,新状态再添加,否则一定会出现"两个都亮"或"一个都不亮"。
classList优于className,className优于style——样式少用style,样式多用类,且类名操作一律走classList。- 表单元素的布尔属性(
disabled/checked/selected)只认布尔值,任何字符串都会被当成"真",这是最隐蔽也最常见的坑。 - 自定义属性是"把数据挂在元素上"的官方方案,配合事件里的
dataset取值,可以让"点哪个就操作哪个"不再依赖闭包或全局变量。 - 三个操作元素样式的入口各有其位:改一两句 →
style;改一大片 → 预定义类 +classList;写代码前先判断属于哪一种,比事后调试省时间。