在Web开发中,动态操作DOM(文档对象模型)是核心技能之一。通过JavaScript修改HTML结构,可以实现无刷新更新页面、动态加载内容、创建交互式组件等关键功能。本文ZHANID工具网将从基础操作到实战案例,系统讲解如何使用JavaScript动态添加和删除HTML代码,涵盖原生API和现代框架思想,并提供可复用的代码片段。
一、DOM操作基础:选择元素与创建节点
(一)选择DOM元素
在操作DOM前,需先获取目标元素的引用。常用方法包括:
// 1. 通过ID获取(返回单个元素)
const header = document.getElementById('main-header');
// 2. 通过类名获取(返回HTMLCollection)
const buttons = document.getElementsByClassName('btn');
// 3. 通过标签名获取(返回HTMLCollection)
const paragraphs = document.getElementsByTagName('p');
// 4. 通过CSS选择器获取(返回第一个匹配元素)
const firstItem = document.querySelector('.list-item:first-child');
// 5. 通过CSS选择器获取所有匹配元素(返回NodeList)
const allItems = document.querySelectorAll('.list-item');性能提示:
避免在循环中频繁调用
querySelector,可缓存选择结果getElementById性能最高,优先使用ID选择器NodeList和HTMLCollection的区别:前者可通过forEach遍历,后者需转换为数组
(二)创建新节点
动态添加内容前需先创建DOM节点:
// 1. 创建元素节点
const newDiv = document.createElement('div');
newDiv.className = 'dynamic-box';
newDiv.id = 'box-1';
// 2. 创建文本节点
const textNode = document.createTextNode('这是动态添加的内容');
// 3. 创建带文本的元素(推荐方式)
const newParagraph = document.createElement('p');
newParagraph.textContent = '这是更简洁的创建方式';
// 4. 创建包含HTML结构的元素
const newCard = document.createElement('div');
newCard.innerHTML = `
<div class="card">
<h3>卡片标题</h3>
<p>卡片内容...</p>
</div>
`;安全警告:
使用
innerHTML时需防范XSS攻击,避免直接插入用户输入对用户输入应使用
textContent或转义处理
二、动态添加HTML的5种方法
(一)appendChild() - 追加到末尾
const parent = document.getElementById('container');
const child = document.createElement('p');
child.textContent = '我是新添加的子元素';
parent.appendChild(child); // 添加到父元素末尾应用场景:
动态加载评论列表
无限滚动加载更多内容
(二)insertBefore() - 在指定元素前插入
const parent = document.getElementById('container');
const referenceNode = document.querySelector('#container p:first-child');
const newNode = document.createElement('div');
newNode.textContent = '插入到第一个段落前';
parent.insertBefore(newNode, referenceNode);兼容性提示:
IE8及以下版本不支持
NodeList.forEach,需先转换为数组
(三)insertAdjacentHTML() - 灵活插入HTML字符串
const element = document.getElementById('target');
// 参数说明:
// 'beforebegin' - 元素自身的前面
// 'afterbegin' - 元素内部的第一个子节点之前
// 'beforeend' - 元素内部的最后一个子节点之后
// 'afterend' - 元素自身的后面
element.insertAdjacentHTML('beforeend', '<li>新项目</li>');性能优势:
比
innerHTML拼接更高效,避免重复解析整个元素支持精确控制插入位置
(四)innerHTML - 批量替换内容
const container = document.getElementById('content');
// 完全替换内容
container.innerHTML = `
<div class="new-content">
<h2>新标题</h2>
<p>完全替换原有内容</p>
</div>
`;
// 追加内容(需拼接原有HTML)
container.innerHTML += '<p>追加的新段落</p>';注意事项:
频繁操作
innerHTML会导致页面重绘,性能较差会破坏元素上的事件监听器
(五)DocumentFragment - 批量操作优化
const fragment = document.createDocumentFragment();
// 创建100个div并添加到fragment
for (let i = 0; i < 100; i++) {
const div = document.createElement('div');
div.textContent = `项目 ${i}`;
fragment.appendChild(div);
}
// 一次性添加到DOM
document.getElementById('list').appendChild(fragment);性能原理:
DocumentFragment存在于内存中,不触发页面重排
适合需要批量添加大量元素的场景
三、动态删除HTML的3种方法
(一)removeChild() - 移除指定子元素
const parent = document.getElementById('container');
const child = document.getElementById('child-to-remove');
if (child && parent.contains(child)) {
parent.removeChild(child);
}安全实践:
删除前检查元素是否存在
使用
parent.contains()避免操作不存在的元素
(二)remove() - 现代浏览器简化方法
const element = document.getElementById('obsolete-element');
element?.remove(); // 可选链操作符避免报错兼容性说明:
IE不支持
remove()方法,需使用removeChild()替代可通过以下polyfill实现兼容:
if (!Element.prototype.remove) {
Element.prototype.remove = function() {
if (this.parentNode) {
this.parentNode.removeChild(this);
}
};
}(三)innerHTML清空内容
const container = document.getElementById('dynamic-area');
container.innerHTML = ''; // 清空所有子元素性能对比:
对于大量元素,
innerHTML = ''比循环removeChild()更高效但会丢失所有子元素的事件监听器

四、实战案例:动态待办事项列表
(一)HTML结构
<div class="container"> <h1>待办事项</h1> <input type="text" id="todo-input" placeholder="输入新任务"> <button id="add-btn">添加</button> <ul id="todo-list"></ul> </div>
(二)JavaScript实现
// 获取DOM元素
const todoInput = document.getElementById('todo-input');
const addBtn = document.getElementById('add-btn');
const todoList = document.getElementById('todo-list');
// 添加待办事项
function addTodo() {
const text = todoInput.value.trim();
if (!text) return;
// 创建新列表项
const li = document.createElement('li');
li.className = 'todo-item';
li.innerHTML = `
<span>${text}</span>
<button class="delete-btn">删除</button>
`;
// 添加到列表
todoList.appendChild(li);
// 清空输入框
todoInput.value = '';
}
// 删除待办事项
function deleteTodo(event) {
if (event.target.classList.contains('delete-btn')) {
const li = event.target.closest('li');
if (li) {
li.remove(); // 或使用 li.parentNode.removeChild(li) 兼容IE
}
}
}
// 事件监听
addBtn.addEventListener('click', addTodo);
todoList.addEventListener('click', deleteTodo);
// 回车键提交
todoInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
addTodo();
}
});(三)功能扩展
本地存储持久化:
// 保存到localStorage
function saveTodos() {
const todos = Array.from(todoList.children).map(li => {
return li.querySelector('span').textContent;
});
localStorage.setItem('todos', JSON.stringify(todos));
}
// 从localStorage加载
function loadTodos() {
const savedTodos = JSON.parse(localStorage.getItem('todos') || '[]');
savedTodos.forEach(text => {
// 重复添加逻辑...
});
}
// 在addTodo和deleteTodo中调用saveTodos
// 页面加载时调用loadTodos标记完成状态:
// 修改addTodo中的li创建部分
li.innerHTML = `
<input type="checkbox" class="complete-cb">
<span>${text}</span>
<button class="delete-btn">删除</button>
`;
// 添加完成状态切换事件
todoList.addEventListener('change', (e) => {
if (e.target.classList.contains('complete-cb')) {
const span = e.target.nextElementSibling;
span.style.textDecoration = e.target.checked ? 'line-through' : 'none';
}
});五、性能优化与最佳实践
(一)减少DOM操作次数
错误示例:
// 每次循环都操作DOM,性能极差
for (let i = 0; i < 100; i++) {
const div = document.createElement('div');
document.body.appendChild(div);
}优化方案:
// 使用DocumentFragment批量操作
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const div = document.createElement('div');
fragment.appendChild(div);
}
document.body.appendChild(fragment);(二)事件委托优化事件处理
传统方式:
// 为每个删除按钮单独添加事件
document.querySelectorAll('.delete-btn').forEach(btn => {
btn.addEventListener('click', deleteTodo);
});优化方案:
// 利用事件冒泡在父元素上处理
todoList.addEventListener('click', (e) => {
if (e.target.classList.contains('delete-btn')) {
e.target.closest('li').remove();
}
});优势:
减少事件监听器数量
动态添加的元素自动拥有事件处理
(三)虚拟滚动处理长列表
核心思想:
只渲染可视区域内的元素
监听滚动事件动态调整显示内容
简化实现示例:
const container = document.getElementById('scroll-container');
const itemHeight = 50; // 每个项目高度
const visibleCount = Math.ceil(container.clientHeight / itemHeight);
let startIndex = 0;
function renderVisibleItems() {
const scrollTop = container.scrollTop;
const newStartIndex = Math.floor(scrollTop / itemHeight);
if (newStartIndex !== startIndex) {
startIndex = newStartIndex;
const fragment = document.createDocumentFragment();
// 计算需要渲染的项目范围
const endIndex = Math.min(startIndex + visibleCount * 2, data.length);
for (let i = startIndex; i < endIndex; i++) {
const item = document.createElement('div');
item.style.height = `${itemHeight}px`;
item.textContent = data[i];
fragment.appendChild(item);
}
container.innerHTML = ''; // 清空容器
container.appendChild(fragment);
}
requestAnimationFrame(renderVisibleItems);
}
// 初始化渲染
renderVisibleItems();
container.addEventListener('scroll', renderVisibleItems);六、常见问题解决方案
(一)动态内容不显示
可能原因:
脚本在DOM加载前执行
选择器错误
元素被CSS隐藏
解决方案:
// 1. 确保DOM已加载
document.addEventListener('DOMContentLoaded', function() {
// 你的代码
});
// 或使用defer属性加载脚本
// <script src="app.js" defer></script>
// 2. 检查选择器
console.log(document.querySelector('#non-existent')); // 应为null
// 3. 检查CSS
console.log(window.getComputedStyle(element).display);(二)事件监听器失效
常见场景:
使用
innerHTML覆盖元素导致事件丢失动态添加的元素未绑定事件
解决方案:
// 方案1:使用事件委托(推荐)
document.getElementById('parent').addEventListener('click', (e) => {
if (e.target.matches('.dynamic-child')) {
// 处理点击
}
});
// 方案2:重新绑定事件(不推荐)
function addItem() {
const item = document.createElement('div');
item.className = 'dynamic-child';
item.textContent = '新项目';
// 添加到DOM后绑定事件
document.getElementById('parent').appendChild(item);
item.addEventListener('click', handleClick);
}(三)内存泄漏
常见原因:
移除元素时未清理事件监听器
闭包持有对DOM元素的引用
解决方案:
// 1. 移除元素前清理事件
function cleanup() {
const element = document.getElementById('to-remove');
// 移除所有事件监听器(需自行维护监听器列表)
element.removeAllEventListeners?.(); // 非标准方法,实际需手动管理
// 标准做法:在移除前执行清理函数
element.remove();
}
// 2. 避免闭包引用
function createElement() {
const element = document.createElement('div');
// 错误示例:闭包持有element引用
// element.addEventListener('click', function() {
// console.log(element); // 即使元素已移除,引用仍存在
// });
// 正确做法:使用弱引用或事件委托
}结语
动态操作DOM是JavaScript的核心能力,掌握这些技术可以创建丰富的交互式Web应用。从基础的appendChild到高性能的虚拟滚动,每种方法都有其适用场景。在实际开发中,应结合项目需求选择最优方案,并注意性能优化和内存管理。通过本文介绍的实战案例和最佳实践,读者可以系统地提升DOM操作技能,编写出更高效、更可维护的JavaScript代码。
本文由@战地网 原创发布。
该文章观点仅代表作者本人,不代表本站立场。本站不承担相关法律责任。
如若转载,请注明出处:https://www.zhanid.com/biancheng/5053.html




















