5个JavaScript入门实战小案例:巩固基础语法
通过交互式计数器、汇率换战器、动态待办清单、实时字数统计与随机名言生成器5个经典入门案例,系统巩固JavaScript变量、函数、DOM操作、事件流及数组对象等核心基础语法。
初学 JavaScript 时,很多学习者能够熟记变量声明、循环语句或函数语法的概念,但一面对空白的代码编辑器,往往不知道如何把这些孤立的知识点组合成能工作的实际功能。巩固基础语法的最佳途径,不是反复死记API手册,而是在明确的交互目标驱动下,完成结构小巧、针对性强的微型实战项目。本文提炼出 5 个入门级实战小案例,分别聚焦不同维度的语法核心,帮助你在真实编码中理清数据流动与界面渲染的逻辑。
一、案例设计与核心语法映射
一个合格的入门练习应该兼顾直观性与知识密度。5 个案例从最简单的单一状态控制,逐步过渡到多数据聚合与逻辑抽象,涵盖了前端交互开发中最常用的基础语法模块。
二、入门案例的通用开发与调试流程
动手写代码之前,先建立规范的开发次序,可以有效避免“页面报错却不知从何查起”的困境。无论是简单的计数器还是多节点渲染的待办列表,标准的前端微案例开发均遵循固定的五步流水线。
三、案例一:交互式数值计数器
计数器是所有前端状态管理的基石。它的目标是提供加、减、重置按钮,改变界面上显示的数值,并在数值达到正负临界时通过颜色区分状态。
1. 巩固知识点
- 变量声明:掌握
const(固定DOM引用)与let(可变计数状态)的使用边界。 - DOM操作:使用
document.querySelector选取目标元素,使用textContent修改页面文本。 - 事件监听:通过
addEventListener('click', callback)处理用户点击。
2. 代码实现
<!-- HTML 结构 -->
<div class="counter-box">
<h3>当前计数:<span id="count-display">0</span></h3>
<button id="btn-decrement">减少 -1</button>
<button id="btn-reset">重置 0</button>
<button id="btn-increment">增加 +1</button>
</div>
<script>
// 1. 获取DOM节点
const display = document.querySelector('#count-display');
const btnDecrement = document.querySelector('#btn-decrement');
const btnReset = document.querySelector('#btn-reset');
const btnIncrement = document.querySelector('#btn-increment');
// 2. 定义状态变量
let count = 0;
// 3. 封装渲染函数
function updateDisplay() {
display.textContent = count;
if (count > 0) {
display.style.color = '#2e7d32'; // 正数绿色
} else if (count < 0) {
display.style.color = '#d32f2f'; // 负数红色
} else {
display.style.color = '#1e293b'; // 归零常规色
}
}
// 4. 事件监听与状态变更
btnIncrement.addEventListener('click', function () {
count++;
updateDisplay();
});
btnDecrement.addEventListener('click', function () {
count--;
updateDisplay();
});
btnReset.addEventListener('click', function () {
count = 0;
updateDisplay();
});
</script>
display.textContent++。虽然 JavaScript 的隐式类型转换可能会在某些场景下计算成功,但它会使原本属于数字类型的状态和属于字符串类型的 DOM 内容耦合在一起,埋下类型混乱的隐患。始终保持“内部状态变量计算完毕,再显式同步给视图”的模式。
四、案例二:简易汇率换算器
该案例接收用户输入的金额(如人民币),选择换算目标(如美元、欧元),点击换算后输出对应格式化金额。核心在于对表单输入值的提取、数据类型转换以及防御性校验。
1. 巩固知识点
- 输入值类型转换:表单元素的
input.value默认返回字符串,需要通过parseFloat()或Number()显式转换。 - 数值校验:使用
Number.isNaN()检验输入是否合法,防止计算输出NaN。 - 数值格式化:使用
Number.prototype.toFixed(2)保留两位小数。
2. 代码实现
<!-- HTML 结构 -->
<div class="converter-box">
<input type="number" id="cny-input" placeholder="输入人民币金额(CNY)" />
<select id="target-currency">
<option value="0.14">美元 (USD, 汇率 0.14)</option>
<option value="0.13">欧元 (EUR, 汇率 0.13)</option>
<option value="21.5">日元 (JPY, 汇率 21.5)</option>
</select>
<button id="btn-convert">开始换算</button>
<p id="convert-result">折合金额:--</p>
</div>
<script>
const inputEl = document.querySelector('#cny-input');
const currencySelect = document.querySelector('#target-currency');
const convertBtn = document.querySelector('#btn-convert');
const resultText = document.querySelector('#convert-result');
// 纯函数:计算并返回换算结果
function calculateExchange(amount, rate) {
return (amount * rate).toFixed(2);
}
convertBtn.addEventListener('click', function () {
const rawVal = inputEl.value.trim();
const amount = parseFloat(rawVal);
const rate = parseFloat(currencySelect.value);
// 边界与防御性检查
if (rawVal === '' || Number.isNaN(amount) || amount < 0) {
resultText.textContent = '错误:请输入大于等于0的合法金额!';
resultText.style.color = '#d32f2f';
return;
}
const convertedVal = calculateExchange(amount, rate);
const currencyName = currencySelect.options[currencySelect.selectedIndex].text.split(' ')[0];
resultText.textContent = `折合金额:${convertedVal} ${currencyName}`;
resultText.style.color = '#345e82';
});
</script>
input.value 哪怕设置了 type="number",在 JS 层面读取出来的也是 string。如果不做显式转换而直接进行加法,会发生字符串拼接(例如 "100" + 0.14 = "1000.14")。虽然乘法会触发隐式转换,但养成显式声明类型的习惯可以规避绝大多数隐蔽 Bug。
五、案例三:动态待办事项清单(Todo List)
待办列表是训练 DOM 树动态增删与数组同步的试金石。它涵盖输入新项目、生成 DOM 节点追加到页面、点击标记完成以及从列表中彻底移除节点。
1. 巩固知识点
- 动态创建节点:
document.createElement('li')。 - DOM树装配:
parentNode.appendChild(childNode)。 - 事件委托(Event Delegation):不在每个子节点单独监听,而是在父级
<ul>上统一监听,通过e.target区分具体点击来源。 - 字符串清洗:使用
String.prototype.trim()过滤空白提交。
2. 代码实现
<!-- HTML 结构 -->
<div class="todo-app">
<input type="text" id="todo-input" placeholder="输入待办事项后回车..." />
<button id="todo-add-btn">添加事项</button>
<ul id="todo-list"></ul>
</div>
<script>
const todoInput = document.querySelector('#todo-input');
const addBtn = document.querySelector('#todo-add-btn');
const todoList = document.querySelector('#todo-list');
function createTodoItem(text) {
const li = document.createElement('li');
li.className = 'todo-item';
const span = document.createElement('span');
span.textContent = text;
span.className = 'todo-text';
const deleteBtn = document.createElement('button');
deleteBtn.textContent = '删除';
deleteBtn.className = 'btn-delete';
li.appendChild(span);
li.appendChild(deleteBtn);
return li;
}
function handleAdd() {
const text = todoInput.value.trim();
if (text === '') {
alert('待办内容不能为空');
return;
}
const itemNode = createTodoItem(text);
todoList.appendChild(itemNode);
todoInput.value = ''; // 清空输入框
todoInput.focus();
}
// 点击添加按钮
addBtn.addEventListener('click', handleAdd);
// 监听回车键添加
todoInput.addEventListener('keydown', function (e) {
if (e.key === 'Enter') {
handleAdd();
}
});
// 事件委托:在父级 ul 上监听所有子项的操作
todoList.addEventListener('click', function (e) {
const target = e.target;
// 如果点击的是删除按钮,移除其所属的 li 节点
if (target.classList.contains('btn-delete')) {
const li = target.parentElement;
li.remove();
}
// 如果点击的是文字本身,切换完成下划线样式
if (target.classList.contains('todo-text')) {
target.style.textDecoration =
target.style.textDecoration === 'line-through' ? 'none' : 'line-through';
target.style.color =
target.style.textDecoration === 'line-through' ? '#94a3b8' : '#1e293b';
}
});
</script>
六、案例四:实时字符计数与输入截断提醒
该案例模拟常见于社交平台或表单备注框的字数限制功能。用户在输入框内键入文字时,实时显示当前字数与剩余允许字数;当超出阈值时,自动触发视觉警示并禁止提交。
1. 巩固知识点
- 输入事件类型:使用
input事件而非keydown或change。input事件在每一次文字输入、粘贴、删除时都会即时触发。 - 字符串长度:通过
str.length获取实际字符数。 - 动态样式类操作:使用
element.classList.add()、remove()或toggle()切换预置 CSS 类。 - 模板字符串:使用反引号
`当前输入 ${cur} / ${max}`构建动态提示文案。
2. 代码实现
<!-- HTML 结构 -->
<div class="char-counter-box">
<textarea id="feedback-text" rows="4" placeholder="请输入您的意见(最多100字)..."></textarea>
<div class="counter-status">
<span id="char-info">0 / 100 字</span>
<span id="warning-msg" style="color: #d32f2f; display: none;">已超出最大限制!</span>
</div>
</div>
<script>
const textarea = document.querySelector('#feedback-text');
const charInfo = document.querySelector('#char-info');
const warningMsg = document.querySelector('#warning-msg');
const MAX_LIMIT = 100;
textarea.addEventListener('input', function () {
const currentLength = textarea.value.length;
const isExceeded = currentLength > MAX_LIMIT;
// 动态更新文本信息
charInfo.textContent = `${currentLength} / ${MAX_LIMIT} 字`;
if (isExceeded) {
charInfo.style.color = '#d32f2f';
warningMsg.style.display = 'inline';
textarea.style.borderColor = '#d32f2f';
} else {
charInfo.style.color = '#64748b';
warningMsg.style.display = 'none';
textarea.style.borderColor = '#e2e8f0';
}
});
</script>
change 事件监听输入框。change 仅在输入框失去焦点(Blur)时才触发,无法做到“打一个字变一下数字”的实时同步体验;而 input 无论键盘打字、拼音选词还是鼠标右键粘贴,都会立刻产生事件通知。
七、案例五:随机名言展示器
该案例维护一个由多个对象构成的数组,每个对象包含名言内容与作者信息。点击“换一句”按钮时,程序利用数学内置对象生成不重复或随机的下标,提取对应对象并渲染到页面卡片中。
1. 巩固知识点
- 对象数组(Array of Objects):理解
[ { quote: '...', author: '...' } ]的数据组织结构。 - Math 内置对象:掌握
Math.random()(生成 0 到 1 之间的浮点数)与Math.floor()(向下取整)组合获取随机数组索引。 - 对象属性访问:掌握点语法
obj.quote与obj.author。
2. 代码实现
<!-- HTML 结构 -->
<div class="quote-card">
<blockquote id="quote-text">点击下方按钮获取名言</blockquote>
<p id="quote-author" style="text-align: right; color: #64748b;">--</p>
<button id="btn-next-quote">随机换一句</button>
</div>
<script>
// 1. 数据源:对象数组
const quoteList = [
{ text: "代码是写给人看的,顺便能在机器上运行。", author: "Harold Abelson" },
{ text: "简单胜于复杂,清晰胜于隐晦。", author: "Tim Peters" },
{ text: "先让它跑起来,再让它做正确,最后让它变快。", author: "Kent Beck" },
{ text: "控制复杂度是计算机科学最本质的目标。", author: "Edsger W. Dijkstra" },
{ text: "优秀的程序员不是写代码最多的,而是删代码最多的。", author: "业内谚语" }
];
const quoteTextEl = document.querySelector('#quote-text');
const quoteAuthorEl = document.querySelector('#quote-author');
const nextBtn = document.querySelector('#btn-next-quote');
let lastIndex = -1;
function renderRandomQuote() {
let randomIndex;
// 循环判断:确保两次连续点击不会随机到同一条数据
do {
randomIndex = Math.floor(Math.random() * quoteList.length);
} while (quoteList.length > 1 && randomIndex === lastIndex);
lastIndex = randomIndex;
const currentQuote = quoteList[randomIndex];
quoteTextEl.textContent = `“${currentQuote.text}”`;
quoteAuthorEl.textContent = `—— ${currentQuote.author}`;
}
nextBtn.addEventListener('click', renderRandomQuote);
// 页面加载完成自动展示第一条
renderRandomQuote();
</script>
八、基础语法巩固总结与进阶路线
通过上述 5 个案例的练习,可以清楚看到一个典型 JavaScript 前端交互的运转脉络:以数据为中心,以事件为诱因,以 DOM 为媒介。
| 案例名称 | 涉及的数据结构 | 关键 DOM / Web API | 巩固的语言特性 |
|---|---|---|---|
| 交互式计数器 | 基础数值 (Number) | querySelector, textContent, click | let/const 作用域、算术运算符 |
| 汇率换算器 | 浮点数、字符串 | value, toFixed, options | parseFloat、isNaN 防御性校验、纯函数 |
| 动态待办清单 | DOM 节点树、事件对象 | createElement, appendChild, remove | 事件冒泡、事件委托、trim 清洗 |
| 字符实时统计 | 字符串 (String) | input 事件, style, classList | length 属性、三元表达式、模板字面量 |
| 随机名言生成 | 对象数组 (Array of Objects) | Math.random, Math.floor | 数组索引寻址、do-while 循环、对象属性提取 |
下一步练习建议
- 尝试数据持久化:为“待办清单”案例接入
localStorage.setItem()与localStorage.getItem(),实现刷新浏览器后数据不丢失。 - 尝试网络数据接入:为“随机名言”案例引入
fetch(),从公共 API 异步拉取真实的名言列表,体会异步编程与网络加载状态的处理。 - 重构为模块化结构:尝试把每个小案例的代码按“状态定义、事件绑定、渲染函数”分成不同文件或模块进行引入,提前适应模块化工程思维。
JS在setTimeout或Ajax回调中打开新标签页失败怎么处理
2026-11-05 15:50:12
JS直播协议选型:HLS、FLV与WebRTC的延迟与兼容性对比
2026-11-05 12:16:14
JS实现回到顶部:scrollTop赋值与浏览器兼容性处理
2026-11-04 23:43:35
JS使用classList动态添加、移除和切换CSS类名
2026-11-04 23:40:58
在 D3.js 中实现平滑过渡动画的技巧
2026-11-03 23:46:09








