通过点击计数器、待办清单、实时字数统计、手风琴折叠面板与模态弹窗5个典型实战项目,循序渐进掌握元素选择、事件绑定、节点创建、样式切换与事件冒泡等DOM核心操作。
5个JavaScript迷你项目实战,快速掌握DOM操作基础
时间:2026-10-30
浏览:380
文章目录
掌握文档对象模型(DOM)是前端交互开发的基石。本文通过点击计数器、待办清单、实时字数统计器、手风琴折叠面板与模态弹窗5个轻量项目,按照“元素选择、事件绑定、状态变更、视图更新”的实际链路,带你系统建立DOM操作的核心心智模型与代码直觉。
一、DOM树与JavaScript交互原理
当浏览器解析HTML文档时,会将其构建为一个由节点构成的树状结构——DOM树。JavaScript本身并不具备渲染网页画面的能力,它必须通过浏览器提供的DOM API(如 document.querySelector、addEventListener 等)作为桥梁,读取页面上的标签属性、监听用户行为并实时修改结构或内容。
初学者常犯的错误是将界面状态直接与HTML文本强耦合,导致代码在修改多个地方时出现遗漏。理解DOM的关键在于把握“状态驱动界面”的思想:用户的每一次点击或输入,先触发JavaScript事件处理函数,更新内部数据变量,最后再将计算后的最新结果同步给DOM节点。
二、项目一:点击计数器(Counter)
点击计数器是理解“元素获取、事件监听、数据状态同步”的最经典案例。我们在此项目中练习如何读取一个数字、响应加减与重置按钮,并更新界面文本。
1. 核心HTML结构
<div class="counter-box">
<h3 id="count-display">0</h3>
<button id="btn-decrease">-1</button>
<button id="btn-reset">重置</button>
<button id="btn-increase">+1</button>
</div>
2. JavaScript 实现
// 1. 获取需要操作的 DOM 节点
const countDisplay = document.querySelector('#count-display');
const btnDecrease = document.querySelector('#btn-decrease');
const btnReset = document.querySelector('#btn-reset');
const btnIncrease = document.querySelector('#btn-increase');
// 2. 维护独立的状态变量,避免从 DOM 中反向读取字符串
let count = 0;
// 3. 封装渲染函数:让界面严格反映当前状态
function updateDisplay() {
countDisplay.textContent = count;
// 根据数值正负变化显示样式
if (count > 0) {
countDisplay.style.color = '#71A397';
} else if (count < 0) {
countDisplay.style.color = '#E06C75';
} else {
countDisplay.style.color = '#1E293B';
}
}
// 4. 为每个按钮绑定独立的点击事件处理程序
btnIncrease.addEventListener('click', () => {
count += 1;
updateDisplay();
});
btnDecrease.addEventListener('click', () => {
count -= 1;
updateDisplay();
});
btnReset.addEventListener('click', () => {
count = 0;
updateDisplay();
});
textContent 而不是 innerHTML?因为计数器只包含纯文本内容。使用 innerHTML 会触发浏览器的 HTML 解析器,既消耗多余性能,又在面对不受信数据时存在潜在的 XSS 安全风险。
三、项目二:简易待办清单(Todo List)
待办清单的核心是学习“动态生成新节点(createElement)”、“挂载到父节点(appendChild)”以及“节点销毁(remove)”。此外,项目还涉及表单默认提交行为的拦截。
1. 核心HTML结构
<form id="todo-form">
<input type="text" id="todo-input" placeholder="输入待办事项并回车..." required />
<button type="submit">添加</button>
</form>
<ul id="todo-list"></ul>
2. JavaScript 实现
const todoForm = document.querySelector('#todo-form');
const todoInput = document.querySelector('#todo-input');
const todoList = document.querySelector('#todo-list');
todoForm.addEventListener('submit', (e) => {
// 阻止表单提交引起的页面刷新
e.preventDefault();
const text = todoInput.value.trim();
if (text === '') return;
// 1. 创建列表外层 li 元素
const li = document.createElement('li');
li.className = 'todo-item';
// 2. 创建文本描述 span
const textSpan = document.createElement('span');
textSpan.textContent = text;
// 点击文本完成/取消标记
textSpan.addEventListener('click', () => {
textSpan.classList.toggle('completed');
});
// 3. 创建删除按钮
const delBtn = document.createElement('button');
delBtn.textContent = '删除';
delBtn.className = 'btn-delete';
delBtn.addEventListener('click', () => {
li.remove(); // 销毁整个 li 节点
});
// 4. 组装并挂载到待办列表中
li.appendChild(textSpan);
li.appendChild(delBtn);
todoList.appendChild(li);
// 5. 清空输入框并重新聚焦
todoInput.value = '';
todoInput.focus();
});
四、项目三:实时字数统计器(Character Counter)
这个项目展示了如何响应连续的高频用户输入(input 事件),并依据剩余字数动态更新界面的文字与警告样式。
1. 核心HTML结构
<div class="char-box">
<textarea id="text-input" placeholder="请输入内容,限100字以内..." maxlength="100"></textarea>
<div class="counter-status">
已输入 <span id="char-current">0</span> / <span id="char-max">100</span> 字
<span id="warning-msg" style="display: none; color: #E06C75;">已达到上限!</span>
</div>
</div>
2. JavaScript 实现
const textInput = document.querySelector('#text-input');
const charCurrent = document.querySelector('#char-current');
const warningMsg = document.querySelector('#warning-msg');
const MAX_LENGTH = 100;
// 使用 'input' 事件:只要输入框内容发生变化(包括粘贴、打字、删除)便立即触发
textInput.addEventListener('input', () => {
const currentLength = textInput.value.length;
// 更新当前计数字符
charCurrent.textContent = currentLength;
// 根据字数动态切换警告状态
if (currentLength >= MAX_LENGTH) {
warningMsg.style.display = 'inline';
charCurrent.style.color = '#E06C75';
textInput.style.borderColor = '#E06C75';
} else if (currentLength >= MAX_LENGTH * 0.8) {
warningMsg.style.display = 'none';
charCurrent.style.color = '#D19A66';
textInput.style.borderColor = '#D19A66';
} else {
warningMsg.style.display = 'none';
charCurrent.style.color = '#64748B';
textInput.style.borderColor = '#CBD5E1';
}
});
change 事件只有在输入框失去焦点时才会触发,而 input 事件在每一次按键、拼音确认或粘贴时都会实时触发。实现“即时反馈”功能时,必须优先选择 input 事件。
五、项目四:手风琴折叠面板(Accordion)
手风琴组件是前端最常见的交互模式之一。在这个实战中,我们将掌握通过 querySelectorAll 选取节点列表、遍历绑定事件、读取数据属性以及实现排他展开(即展开当前项时自动折叠其他项)。
1. 核心HTML结构
<div class="accordion">
<div class="accordion-item">
<button class="accordion-header">什么是 DOM?</button>
<div class="accordion-content">DOM 是文档对象模型,是 HTML 文档的编程接口。</div>
</div>
<div class="accordion-item">
<button class="accordion-header">什么是事件委托?</button>
<div class="accordion-content">利用事件冒泡机制,将子元素事件委托给父容器统一监听。</div>
</div>
</div>
2. JavaScript 实现
const headers = document.querySelectorAll('.accordion-header');
headers.forEach((header) => {
header.addEventListener('click', () => {
const currentItem = header.parentElement;
const isActive = currentItem.classList.contains('active');
// 1. 排他处理:先关闭所有已展开的项目
document.querySelectorAll('.accordion-item').forEach((item) => {
item.classList.remove('active');
});
// 2. 如果之前未展开,则激活当前项;反之保持关闭
if (!isActive) {
currentItem.classList.add('active');
}
});
});
六、项目五:模态框弹窗(Modal Popup)
模态弹窗涉及界面层级显示、键盘全局事件捕获(按 ESC 键退出)以及事件冒泡阻断。这是综合性最强的基础实战之一。
1. 核心HTML结构
<button id="open-modal-btn">打开弹窗</button>
<!-- 模态框全屏遮罩 -->
<div id="modal-overlay" class="modal-overlay hidden">
<div class="modal-card">
<h4>重要系统提示</h4>
<p>这是一个使用纯原生 JavaScript 控制的模态弹窗组件。</p>
<button id="close-modal-btn">关闭</button>
</div>
</div>
2. JavaScript 实现
const openModalBtn = document.querySelector('#open-modal-btn');
const closeModalBtn = document.querySelector('#close-modal-btn');
const modalOverlay = document.querySelector('#modal-overlay');
const modalCard = document.querySelector('.modal-card');
function openModal() {
modalOverlay.classList.remove('hidden');
}
function closeModal() {
modalOverlay.classList.add('hidden');
}
// 1. 点击打开按钮显示弹窗
openModalBtn.addEventListener('click', openModal);
// 2. 点击弹窗内部关闭按钮关闭
closeModalBtn.addEventListener('click', closeModal);
// 3. 点击遮罩背景关闭弹窗
modalOverlay.addEventListener('click', (e) => {
// 仅当点击的是遮罩自身时关闭,避免点击内容卡片误关闭
if (e.target === modalOverlay) {
closeModal();
}
});
// 4. 监听全局键盘事件:按下 ESC 键退出
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && !modalOverlay.classList.contains('hidden')) {
closeModal();
}
});
e.target === modalOverlay 的严格等值判断,或者在内容卡片上调用 e.stopPropagation(),可以有效解决此问题。
七、5个实战项目技能全景图谱
每个项目都聚焦于特定的核心能力。下表将5个迷你项目与DOM核心操作能力进行了完整归纳:
八、总结与进阶建议
通过这5个小项目,我们掌握了DOM操作的基础语法体系:
- 选择精准化:优先使用标准 CSS 选择器
querySelector和querySelectorAll,语义更清晰统一。 - 状态优先:业务逻辑中的数字、列表数组和布尔开关应作为 JavaScript 内存变量维护,而非直接从 DOM 节点的文本中来回截取。
- 样式解耦:通过
classList.add()、classList.remove()和classList.toggle()管理 CSS 类名,避免在 JS 中直接拼接复杂的内联样式字符串。 - 善用委托:对于包含大量子项或动态新增项的列表,将事件委托到父容器可以大幅降低内存消耗并简化生命周期管理。
动手在本地编辑器中敲一遍上述代码并尝试为其添加新特性(例如为待办事项增加本地存储 localStorage 持久化),是快速巩固原生 DOM 能力最扎实的路径。
实用工具推荐
绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
选择画质和输出格式,预览压缩前后的大小。