文章目录
对 DOM 节点进行增删改查(CRUD)是前端工程师的核心技能之一,无论是实现动态交互、数据渲染还是页面更新,都离不开这些操作。本文将详细介绍 DOM 节点的各类操作方法,结合实例解析其用法与注意事项。
一、创建节点
1.1 createElement() 创建新的元素节点
// 创建元素节点
const div = document.createElement('div');
const p = document.createElement('p');
const img = document.createElement('img');
// 设置属性
div.id = 'new-div';
p.className = 'text-paragraph';
img.src = 'image.jpg';
img.alt = '图片描述';
// 设置内容
p.textContent = '这是一个新创建的段落';
// 添加样式
div.style.color = 'blue';
div.style.fontSize = '16px';
1.2 createTextNode() 创建新的文本节点
// 创建文本节点
const textNode = document.createTextNode('这是一段文本内容');
// 将文本节点添加到元素中
const p = document.createElement('p');
p.appendChild(textNode);
通常情况下,我们可以直接通过textContent
或innerHTML
设置元素内容,无需单独创建文本节点,但createTextNode
在某些特殊场景下更有用(如需要精确控制文本节点时)。
1.3 createDocumentFragment()
创建文档片段(DocumentFragment
),这是一个轻量级的文档对象,用于临时存储和操作节点,不会影响 DOM 树,可以提高批量操作的性能。
// 创建文档片段
const fragment = document.createDocumentFragment();
// 向片段中添加多个节点
for (let i = 0; i < 10; i++) {
const li = document.createElement('li');
li.textContent = `项目 ${i + 1}`;
fragment.appendChild(li);
}
// 将片段一次性添加到 DOM中(只触发一次重排)
const ul = document.getElementById('myList');
ul.appendChild(fragment);
注意: 创建的节点默认仅存在于内存中,需插入 DOM 树才会显示。
二、添加节点
2.1 appendChild()
将节点添加到指定父节点的子节点列表的末尾。
如果添加的节点已存在于DOM中,会将其移动到新位置
<ul id="myList">
<li>现有项目1</li>
<li>现有项目2</li>
</ul>
<script>
const list = document.getElementById('myList');
// 创建新节点
const newLi = document.createElement('li');
newLi.textContent = '新项目';
// 添加到列表末尾
list.appendChild(newLi);
// 移动节点
const firstLi = list.firstElementChild;
list.appendChild(firstLi); // 将第一个li移动到列表末尾
</script>
2.2 insertBefore()
在指定的子节点之前插入新节点。
语法:parentNode.insertBefore(newNode, referenceNode)
注意:需要插入到末尾则把第二个参数设为null
<ul id="myList">
<li>项目1</li>
<li>项目3</li>
</ul>
<script>
const list = document.getElementById('myList');
const item3 = list.lastElementChild;
// 创建新节点
const item2 = document.createElement('li');
item2.textContent = '项目2';
// 插入到项目3之前
list.insertBefore(item2, item3);
// 插入到第一个位置(referenceNode为第一个子节点)
const item0 = document.createElement('li');
item0.textContent = '项目0';
list.insertBefore(item0, list.firstElementChild);
// 插入到末尾(referenceNode为null)
const item4 = document.createElement('li');
item4.textContent = '项目4';
list.insertBefore(item4, null); // 效果同appendChild
</script>
2.3 append () 与 prepend ()(现代方法)
HTML5 引入了append()
和prepend()
方法,功能更强大,可以添加节点或字符串。
<div id="container">
<p>现有内容</p>
</div>
<script>
const container = document.getElementById('container');
// append():添加到末尾(可添加多个节点或字符串)
const newP = document.createElement('p');
newP.textContent = '新段落';
container.append(newP, '直接添加的文本');
// prepend():添加到开头
const header = document.createElement('h3');
header.textContent = '标题';
container.prepend(header, '开头文本');
</script>
与 appendChild
的区别:
append()
可以添加字符串(会自动转换为文本节点),appendChild()
只能添加节点append()
没有返回值,appendChild()
返回添加的节点append()
可以一次添加多个节点,appendChild()
一次只能添加一个
兼容性:IE 不支持,现代浏览器(Chrome 54+、Firefox 49 + 等)支持。
三、删除节点
3.1 removeChild()
从父节点中删除指定的子节点。
语法:parentNode.removeChild(childNode)
<ul id="myList">
<li>项目1</li>
<li>项目2</li>
<li>项目3</li>
</ul>
<script>
const list = document.getElementById('myList');
const item2 = list.children[1]; // 获取第二个li
// 删除节点
const removedNode = list.removeChild(item2);
// 可以将删除的节点重新添加到其他位置
myList.appendChild(removedNode);
</script>
如果不确定父节点,可以通过childNode.parentNode
获取:
const node = document.getElementById('to-remove');
if (node && node.parentNode) {
node.parentNode.removeChild(node);
}
3.2 remove ()(现代方法)
直接删除节点自身(无需知道父节点)。
<div id="to-delete">要删除的元素</div>
<script>
const node = document.getElementById('to-delete');
if (node) {
node.remove(); // 直接删除自身
}
</script>
四、 替换节点
replaceChild()
用新节点替换父节点中的某个子节点。
语法:parentNode.replaceChild(newNode, oldNode)
<ul id="myList">
<li>旧项目</li>
<li>项目2</li>
</ul>
<script>
const list = document.getElementById('myList');
const oldItem = list.firstElementChild;
// 创建新节点
const newItem = document.createElement('li');
newItem.textContent = '新项目';
// 替换节点
list.replaceChild(newItem, oldItem);
// 替换后的旧节点可以被重新使用
oldItem.textContent = '被替换的项目';
document.body.appendChild(oldItem);
</script>
五、 克隆节点
cloneNode()
克隆现有节点。
语法:node.cloneNode(deep)
- deep:布尔值,是否深度克隆(克隆所有子节点)
<div id="original">
<p>原始内容</p>
<span>子元素</span>
</div>
<script>
const original = document.getElementById('original');
// 浅克隆(只克隆当前节点,不包括子节点)
const shallowClone = original.cloneNode(false);
console.log(shallowClone.children.length); // 0(没有子节点)
// 深克隆(克隆当前节点及所有子节点)
const deepClone = original.cloneNode(true);
console.log(deepClone.children.length); // 2(包含子节点)
// 手动添加节点
document.body.appendChild(deepClone);
// 注意:克隆节点不会复制事件监听器(除非使用addEventListener且useCapture为true)
original.addEventListener('click', () => alert('原始节点'));
const cloned = original.cloneNode(true);
document.body.appendChild(cloned);
// 点击克隆节点不会触发事件
</script>
注意事项:
- 克隆的节点不会自动添加到 DOM中,需要手动添加
- 克隆节点的id属性会被保留,可能导致文档中出现重复的id(需要手动修改)
- 事件监听器不会被克隆(除非使用旧的onclick等属性)
- 表单元素的
value
(用户输入的内容)在深克隆时会被保留
六、 批量操作节点的性能优化
频繁地操作 DOM(如多次添加、删除节点)会导致浏览器频繁重排(回流)和重绘,严重影响性能。以下是一些优化技巧:
- 使用文档片段(DocumentFragment):
const fragment = document.createDocumentFragment();
// 向片段中添加所有节点
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
div.textContent = `项目 ${i}`;
fragment.appendChild(div);
}
// 一次性添加到DOM
document.body.appendChild(fragment);
- 先将节点从 DOM 中移除,操作完成后再添加回来:
const list = document.getElementById('large-list');
// 临时移除节点
const parent = list.parentNode;
parent.removeChild(list);
// 批量操作(不会触发重排)
for (let i = 0; i < 1000; i++) {
const li = document.createElement('li');
li.textContent = `项目 ${i}`;
list.appendChild(li);
}
// 重新添加到DOM
parent.appendChild(list);
- 隐藏元素后进行操作:
const container = document.getElementById('container');
// 隐藏元素(visibility: hidden不会改变布局,display: none会)
container.style.visibility = 'hidden';
// 执行批量操作
// ...
// 恢复显示
container.style.visibility = 'visible';