一、DOM节点概述
DOM(文档对象模型)将HTML文档表示为节点树,每个部分都是一个节点:
- 元素节点:
<div>、<p>等 - 属性节点:
class、id等 - 文本节点:元素内的文本内容
- 注释节点:
<!-- 注释 -->
二、创建节点
1. 创建元素节点
// 创建div元素
const div = document.createElement('div');
// 创建带属性的元素
const link = document.createElement('a');
link.href = 'https://example.com';
link.textContent = '点击我';
2. 创建文本节点
const text = document.createTextNode('这是一段文本');
// 通常配合元素使用
const paragraph = document.createElement('p');
paragraph.appendChild(text);
3. 创建注释节点
const comment = document.createComment('这是一个注释');
4. 创建文档片段(性能优化)
const fragment = document.createDocumentFragment();
// 批量添加节点到片段
for(let i = 0; i < 100; i++) {
const li = document.createElement('li');
li.textContent = `项目 ${i}`;
fragment.appendChild(li);
}
// 一次性添加到DOM
document.getElementById('list').appendChild(fragment);
三、添加节点
1. appendChild()
const parent = document.getElementById('container');
const child = document.createElement('div');
parent.appendChild(child); // 添加到末尾
2. insertBefore()
const parent = document.getElementById('container');
const newChild = document.createElement('div');
const referenceChild = document.getElementById('reference');
// 在referenceChild之前插入
parent.insertBefore(newChild, referenceChild);
3. insertAdjacentHTML/Element/Text()
const target = document.getElementById('target');
// 四种位置选择
target.insertAdjacentHTML('beforebegin', '<div>前面插入</div>'); // 元素之前
target.insertAdjacentHTML('afterbegin', '<div>内部开头插入</div>'); // 元素内部开头
target.insertAdjacentHTML('beforeend', '<div>内部末尾插入</div>'); // 元素内部末尾
target.insertAdjacentHTML('afterend', '<div>后面插入</div>'); // 元素之后
// insertAdjacentElement 和 insertAdjacentText 用法类似
const newElement = document.createElement('span');
target.insertAdjacentElement('beforeend', newElement);
4. append() 和 prepend()(现代方法)
const container = document.getElementById('container');
// 末尾添加多个节点
container.append('文本', document.createElement('br'), '更多内容');
// 开头添加
container.prepend('这是开头内容');
四、移除节点
1. removeChild()
const parent = document.getElementById('parent');
const child = document.getElementById('child');
// 方法1
parent.removeChild(child);
// 方法2:通过父节点查找
const childToRemove = document.getElementById('child-to-remove');
childToRemove.parentNode.removeChild(childToRemove);
2. remove()(现代方法)
const element = document.getElementById('element-to-remove');
element.remove(); // 直接从DOM中移除
3. 移除所有子节点
const container = document.getElementById('container');
// 方法1:清空innerHTML
container.innerHTML = '';
// 方法2:循环移除(性能更好)
while(container.firstChild) {
container.removeChild(container.firstChild);
}
// 方法3:使用replaceChildren
container.replaceChildren(); // 清空所有子节点
五、移动节点
移动节点本质上是移除再添加到新位置:
// 将节点移动到新位置
const nodeToMove = document.getElementById('movable');
const newParent = document.getElementById('new-parent');
// 方法1:直接添加到新位置(自动从原位置移除)
newParent.appendChild(nodeToMove);
// 方法2:插入到特定位置
const referenceNode = document.getElementById('reference');
newParent.insertBefore(nodeToMove, referenceNode);
// 方法3:使用insertAdjacentElement移动
const target = document.getElementById('target');
target.insertAdjacentElement('beforebegin', nodeToMove);
六、复制节点
1. cloneNode()
const original = document.getElementById('original');
// 浅拷贝:只复制节点本身,不复制子节点
const shallowClone = original.cloneNode(false);
// 深拷贝:复制节点及其所有子节点
const deepClone = original.cloneNode(true);
// 修改克隆节点的ID(避免重复)
deepClone.id = 'original-clone';
// 添加到DOM
document.body.appendChild(deepClone);
2. 克隆并修改
function cloneAndModify(original, modifications) {
const clone = original.cloneNode(true);
// 应用修改
Object.keys(modifications).forEach(key => {
if(key === 'attributes') {
// 修改属性
Object.keys(modifications.attributes).forEach(attr => {
clone.setAttribute(attr, modifications.attributes[attr]);
});
} else if(key === 'textContent') {
// 修改文本内容
clone.textContent = modifications.textContent;
} else if(key === 'children') {
// 处理子节点
modifications.children.forEach((childMod, index) => {
if(clone.children[index]) {
const childClone = cloneAndModify(childMod.element, childMod.modifications);
clone.children[index].replaceWith(childClone);
}
});
}
});
return clone;
}
// 使用示例
const originalItem = document.querySelector('.list-item');
const clonedItem = cloneAndModify(originalItem, {
attributes: { id: 'new-item', class: 'list-item cloned' },
textContent: '新的内容'
});
七、查找节点
1. 经典方法
// 通过ID
const element = document.getElementById('myId');
// 通过类名(返回HTMLCollection)
const elements = document.getElementsByClassName('myClass');
// 通过标签名(返回HTMLCollection)
const divs = document.getElementsByTagName('div');
// 通过name属性
const forms = document.getElementsByName('username');
2. Query Selector方法(推荐)
// 查找单个元素
const firstDiv = document.querySelector('div');
const specialDiv = document.querySelector('#special-id');
const redItem = document.querySelector('.item.red');
// 查找多个元素(返回NodeList)
const allDivs = document.querySelectorAll('div');
const allItems = document.querySelectorAll('.item');
const allInputs = document.querySelectorAll('input[type="text"]');
3. 遍历DOM树
const element = document.getElementById('container');
// 获取父节点
const parent = element.parentNode;
const parentElement = element.parentElement;
// 获取子节点
const children = element.children; // 只包含元素节点
const childNodes = element.childNodes; // 包含所有节点类型
// 第一个和最后一个子节点
const firstChild = element.firstChild;
const firstElementChild = element.firstElementChild;
const lastChild = element.lastChild;
const lastElementChild = element.lastElementChild;
// 兄弟节点
const nextSibling = element.nextSibling;
const nextElementSibling = element.nextElementSibling;
const previousSibling = element.previousSibling;
const previousElementSibling = element.previousElementSibling;
4. 高级查找技巧
// 查找特定内容的元素
const elementsWithText = Array.from(document.querySelectorAll('*')).filter(
el => el.textContent.includes('特定文本')
);
// 查找最近的祖先元素
const closestForm = document.getElementById('input').closest('form');
// 检查元素是否匹配选择器
const isDiv = element.matches('div'); // 返回true/false
// 在特定元素内查找
const container = document.getElementById('container');
const innerDivs = container.querySelectorAll('div');
八、综合示例
<!DOCTYPE html>
<html>
<head>
<style>
.item { padding: 10px; margin: 5px; border: 1px solid #ccc; }
.active { background-color: yellow; }
</style>
</head>
<body>
<div id="container">
<div class="item">项目1</div>
<div class="item">项目2</div>
<div class="item">项目3</div>
</div>
<button id="addBtn">添加项目</button>
<button id="moveBtn">移动项目</button>
<button id="removeBtn">移除最后一个</button>
<button id="cloneBtn">克隆第一个</button>
<script>
// 获取元素
const container = document.getElementById('container');
const addBtn = document.getElementById('addBtn');
const moveBtn = document.getElementById('moveBtn');
const removeBtn = document.getElementById('removeBtn');
const cloneBtn = document.getElementById('cloneBtn');
// 添加新项目
addBtn.addEventListener('click', () => {
const newItem = document.createElement('div');
newItem.className = 'item';
newItem.textContent = `项目 ${container.children.length + 1}`;
// 插入到容器末尾
container.appendChild(newItem);
});
// 移动第一个项目到最后
moveBtn.addEventListener('click', () => {
const firstItem = container.querySelector('.item');
if(firstItem) {
container.appendChild(firstItem); // 移动到最后
}
});
// 移除最后一个项目
removeBtn.addEventListener('click', () => {
const lastItem = container.lastElementChild;
if(lastItem && lastItem.classList.contains('item')) {
lastItem.remove();
}
});
// 克隆第一个项目并修改
cloneBtn.addEventListener('click', () => {
const firstItem = container.querySelector('.item');
if(firstItem) {
const clonedItem = firstItem.cloneNode(true);
clonedItem.textContent = '克隆的: ' + clonedItem.textContent;
clonedItem.classList.add('active');
// 插入到第一个项目后面
firstItem.insertAdjacentElement('afterend', clonedItem);
}
});
// 查找并高亮包含特定文本的项目
function highlightItemsWithText(text) {
const items = container.querySelectorAll('.item');
items.forEach(item => {
if(item.textContent.includes(text)) {
item.classList.add('active');
} else {
item.classList.remove('active');
}
});
}
// 使用查找功能
setTimeout(() => {
highlightItemsWithText('项目2');
}, 2000);
</script>
</body>
</html>
九、性能优化建议
- 批量操作:使用文档片段或
innerHTML批量操作DOM - 离线操作:在内存中完成操作再添加到DOM
- 缓存查询结果:避免重复查询DOM
- 事件委托:在父元素上监听事件,而不是每个子元素
- 避免强制同步布局:避免在循环中读取和写入布局属性
// 不好的做法:多次重排
for(let i = 0; i < 100; i++) {
element.style.width = i + 'px'; // 写操作,触发重排
const width = element.offsetWidth; // 读操作,触发重排
}
// 好的做法:批量操作
const fragment = document.createDocumentFragment();
for(let i = 0; i < 100; i++) {
const div = document.createElement('div');
fragment.appendChild(div);
}
container.appendChild(fragment);
十、兼容性注意事项
- 旧版IE支持:部分方法如
remove()、append()、prepend()在旧版IE中不支持 - NodeList与HTMLCollection:NodeList有forEach方法,HTMLCollection没有
- polyfill方案:对于不支持的方法,可以使用polyfill或传统方法替代
通过掌握这些DOM节点操作方法,你可以灵活地操作网页内容,创建动态交互效果。