练习小项目3:随机正能量语录生成器
你会练到的知识点:
-
数组的创建和随机访问
-
Math.random()
用法 -
DOM 操作:插入文本
项目效果描述:
每次点击按钮,会从一个正能量语录列表中随机选一句展示在页面上。
页面结构(HTML 参考):
<h2>💡 每日一句正能量</h2>
<button id="quoteBtn">生成语录</button>
<p id="quoteDisplay"></p>
实现步骤(按顺序来):
-
创建一个数组,里面存放 5~10 条正能量语录
-
获取按钮和显示区域的 DOM 元素
-
给按钮绑定点击事件
-
每次点击时,随机从数组中取一条
-
将它显示到页面上
-
✅ 提示小技巧:
-
随机下标用:
Math.floor(Math.random() * quotes.length)
-
更新文本用
.textContent
或.innerText
🎯 加分项(选做):
-
每条语录显示时加一个小 emoji,更有趣
-
点击按钮时加个简单的文字动画(如
fade-in
,可用 CSS)
实践代码如下:
JS:
const quotes = ["一想全是问题,一做全是答案","每天进步一点点","允许一切发生","凡事发生,皆有利于我","做人做事,最重要是开心","追随自己内心,不必强求"
]const quoteBtn = document.getElementById('quoteBtn')
const quoteDisplay = document.getElementById('quoteDisplay')quoteBtn.addEventListener('click', () => {const quote = quotes[Math.floor(Math.random() * quotes.length)]quoteDisplay.innerHTML = quotequoteDisplay.classList.remove('fade-in');void quoteDisplay.offsetWidth; // 触发重绘quoteDisplay.classList.add('fade-in');
})
CSS:
.fade-in {animation: fadeIn 0.5s ease-in-out;}@keyframes fadeIn {from {opacity: 0;transform: translateY(10px);}to {opacity: 1;transform: translateY(0);}}
额外知识记录:为什么写void quoteDisplay.offsetWidth;可以触发重绘
在这个项目中,我尝试让点击按钮时“励志语句”出现一个淡入的动画。一开始加了 .fade-in
的类名,但发现点击多次时动画不会重新执行。
于是我追问了这个问题,并通过交流和思考,慢慢搞懂了下面这些内容:
我的问题是:
为什么重复添加相同的类名
.fade-in
,动画不会重新执行?
我的理解过程是:
-
浏览器会把 DOM 操作“合并优化”,不会马上渲染(即等待所有样式操作完成后一起渲染);
-
连续
.remove()
和.add()
在同一帧内发生时,会被认为“最终没变”,所以动画不触发; -
要想“重新触发”,需要强迫浏览器中间“渲染一下”——
我看到了这句神奇的代码:
void element.offsetWidth;
一开始不理解为什么这么写,于是继续追问。
后来我发现:
-
offsetWidth
是一个读取元素布局的属性; -
访问它时会强制触发浏览器的重绘(Reflow);
访问offsetWidth为什么会触发一次浏览器的“强制重绘(reflow)”??
因为浏览器必须确保你访问到的是最新的、准确的布局信息,所以它被迫先完成之前“还没做”的渲染计算(reflow + repaint)。
简单的说就是当你访问offsetWidth时,这时浏览器就不能偷懒了:
“哦,你要读取我页面上这个元素的真实宽度?那我得把之前那堆样式更新都结算一下,再告诉你正确的值。”
所以它会立刻:
- 把还没更新的样式应用上(style flush)
- 计算出最终布局(reflow)
- 然后才返回准确的
offsetWidth
-
所以写
void element.offsetWidth
就能让浏览器中间“刷新一次”,让它记住.fade-in
的移除; -
接下来再加回
.fade-in
,浏览器就能识别成新的动画了!
为什么加
void
?因为我们并不关心这个宽度是多少,只是想强制浏览器“刷新一下渲染状态”。
但加上
void
,可以避免警告、让代码更语义化 —— 明确告诉浏览器:“我只是想让你执行这个读取操作,不需要它的返回值。”
✅ 我最后得出的总结:
动画不重新触发的根本原因:浏览器优化掉了连续的相同 DOM 操作
使用
void element.offsetWidth
:强制触发一次布局刷新让动画重置并重新执行