JavaScript DOM 元素操作全解析:创建 插入与优化 移除 克隆
·
创建 DOM 元素
1. 使用 createElement 方法
// 创建各种类型的元素
const div = document.createElement('div');
const span = document.createElement('span');
const button = document.createElement('button');
const input = document.createElement('input');
const img = document.createElement('img');
// 设置元素属性和内容
div.id = 'myDiv';
div.className = 'container';
div.textContent = 'Hello World';
// 设置其他属性
button.setAttribute('type', 'button');
button.setAttribute('data-action', 'submit');
input.setAttribute('placeholder', 'Enter text');
img.setAttribute('src', 'image.jpg');
img.setAttribute('alt', 'Description');
// 设置样式
div.style.color = 'red';
div.style.fontSize = '16px';
2. 使用 createTextNode 创建文本节点
// 创建纯文本节点
const textNode = document.createTextNode('This is a text node');
// 添加到元素中
const p = document.createElement('p');
p.appendChild(textNode);
// 等价于
p.textContent = 'This is a text node';
3. 使用 innerHTML 创建元素(字符串方式)
const container = document.createElement('div');
container.innerHTML = '<p>Hello <strong>World</strong></p>';
// 注意:innerHTML 会解析整个 HTML 字符串并创建相应元素
// 获取创建的子元素
const pElement = container.querySelector('p');
const strongElement = container.querySelector('strong');
4. 使用 createDocumentFragment 创建文档片段
// 创建文档片段(轻量级容器,不在 DOM 树中)
const fragment = document.createDocumentFragment();
// 在片段中创建多个元素
for (let i = 0; i < 5; i++) {
const li = document.createElement('li');
li.textContent = `Item ${i + 1}`;
fragment.appendChild(li);
}
// 将整个片段插入 DOM(只触发一次重排)
document.getElementById('myList').appendChild(fragment);
插入 DOM 元素
1. 基本插入方法
appendChild
// 将元素添加到父元素的末尾
parentElement.appendChild(newElement);
// 示例
const container = document.getElementById('container');
const newDiv = document.createElement('div');
newDiv.textContent = 'New Element';
container.appendChild(newDiv);
// 可以链式调用
container
.appendChild(document.createElement('div'))
.appendChild(document.createTextNode('Nested text'));
insertBefore
// 在指定元素之前插入新元素
parentElement.insertBefore(newElement, referenceElement);
// 示例:在第一个子元素前插入
const firstChild = parentElement.firstChild;
parentElement.insertBefore(newElement, firstChild);
// 插入到末尾(等同于 appendChild)
parentElement.insertBefore(newElement, null);
// 在最后一个子元素前插入
const lastChild = parentElement.lastChild;
parentElement.insertBefore(newElement, lastChild);
replaceChild
// 替换现有子元素
parentElement.replaceChild(newElement, oldElement);
// 示例
const oldChild = document.getElementById('oldElement');
const newChild = document.createElement('div');
newChild.textContent = 'Replacement Element';
parentElement.replaceChild(newChild, oldChild);
2. 现代插入方法(ES2021+)
append 和 prepend
// 添加到末尾(可添加多个元素或文本)
parentElement.append(newElement1, newElement2, 'Some text');
// 添加到开头
parentElement.prepend(newElement);
// 示例
const container = document.getElementById('container');
const div1 = document.createElement('div');
div1.textContent = 'First';
const div2 = document.createElement('div');
div2.textContent = 'Second';
container.append(div1, div2, ' and some text');
container.prepend('Text at the beginning');
before 和 after
// 在元素之前插入
referenceElement.before(newElement);
// 在元素之后插入
referenceElement.after(newElement);
// 可以插入多个元素
referenceElement.before(element1, element2, 'text node');
// 示例
const targetElement = document.getElementById('target');
const newElement = document.createElement('div');
newElement.textContent = 'Inserted before target';
targetElement.before(newElement);
replaceWith
// 用新元素替换当前元素
oldElement.replaceWith(newElement);
// 可以替换为多个元素
oldElement.replaceWith(element1, element2, 'text');
// 示例
const oldElement = document.getElementById('toBeReplaced');
const newElement = document.createElement('div');
newElement.textContent = 'New replacement element';
oldElement.replaceWith(newElement);
插入优化策略
1. 使用 DocumentFragment 减少重排
// 低效方式:多次操作 DOM
function appendItemsSlow(items, parent) {
items.forEach(item => {
const li = document.createElement('li');
li.textContent = item;
parent.appendChild(li); // 每次都触发重排
});
}
// 高效方式:使用 DocumentFragment
function appendItemsFast(items, parent) {
const fragment = document.createDocumentFragment();
items.forEach(item => {
const li = document.createElement('li');
li.textContent = item;
fragment.appendChild(li);
});
parent.appendChild(fragment); // 只触发一次重排
}
2. 批量操作 DOM
// 低效方式:逐个修改样式
function styleElementsSlow(elements) {
elements.forEach(el => {
el.style.color = 'red';
el.style.fontSize = '16px';
el.style.margin = '10px';
});
}
// 高效方式:通过 CSS 类批量操作
function styleElementsFast(elements) {
elements.forEach(el => {
el.classList.add('styled-element');
});
}
// 对应的 CSS
/*
.styled-element {
color: red;
font-size: 16px;
margin: 10px;
}
*/
3. 使用 innerHTML 与 DOM 操作的权衡
// 对于大量静态内容,innerHTML 更快
function createListWithInnerHTML(items) {
const container = document.getElementById('listContainer');
let html = '<ul>';
items.forEach(item => {
html += `<li>${item}</li>`;
});
html += '</ul>';
container.innerHTML = html;
}
// 对于需要事件监听器的动态内容,DOM 操作更合适
function createListWithDOM(items) {
const container = document.getElementById('listContainer');
const ul = document.createElement('ul');
items.forEach(item => {
const li = document.createElement('li');
li.textContent = item;
li.addEventListener('click', handleClick); // 可以添加事件监听器
ul.appendChild(li);
});
container.appendChild(ul);
}
4. 虚拟滚动和懒加载
// 对于大量列表项,使用虚拟滚动优化性能
class VirtualList {
constructor(container, items, itemHeight) {
this.container = container;
this.items = items;
this.itemHeight = itemHeight;
this.visibleCount = Math.ceil(container.clientHeight / itemHeight) + 5;
this.render();
this.bindScroll();
}
render() {
// 只渲染可见的项目
const startIndex = Math.floor(this.container.scrollTop / this.itemHeight);
const endIndex = Math.min(startIndex + this.visibleCount, this.items.length);
let html = '';
for (let i = startIndex; i < endIndex; i++) {
html += `<div style="height: ${this.itemHeight}px;">${this.items[i]}</div>`;
}
this.container.innerHTML = html;
}
bindScroll() {
this.container.addEventListener('scroll', () => {
this.render();
});
}
}
5. 使用 requestAnimationFrame 优化动画
// 低效的动画方式
function animateElementBad(element) {
let position = 0;
function step() {
position += 2;
element.style.left = position + 'px';
if (position < 200) {
step(); // 可能导致页面卡顿
}
}
step();
}
// 高效的动画方式
function animateElementGood(element) {
let position = 0;
function step() {
position += 2;
element.style.left = position + 'px';
if (position < 200) {
requestAnimationFrame(step); // 浏览器优化的动画帧
}
}
requestAnimationFrame(step);
}
6. 避免强制同步布局
// 低效方式:强制同步布局
function badLayoutExample() {
const element = document.getElementById('myElement');
// 修改样式
element.style.width = '200px';
// 立即读取样式(强制浏览器重新计算布局)
const width = element.offsetWidth;
// 再次修改样式
element.style.height = '200px';
// 再次读取样式
const height = element.offsetHeight;
}
// 高效方式:批量读写操作
function goodLayoutExample() {
const element = document.getElementById('myElement');
// 批量修改样式
element.style.width = '200px';
element.style.height = '200px';
// 批量读取样式
const width = element.offsetWidth;
const height = element.offsetHeight;
}
移除 DOM 元素
1. 使用 removeChild 方法
// 传统方法:通过父元素移除子元素
const parentElement = document.getElementById('parent');
const childElement = document.getElementById('child');
parentElement.removeChild(childElement);
// 安全的移除方式(检查是否存在)
function safeRemoveChild(parent, child) {
if (parent.contains(child)) {
parent.removeChild(child);
}
}
2. 使用 remove 方法(现代浏览器)
// 现代方法:直接移除元素
const element = document.getElementById('toRemove');
element.remove();
// 移除多个元素
document.querySelectorAll('.to-remove').forEach(el => el.remove());
3. 清空元素内容
// 清空元素的所有子元素
const container = document.getElementById('container');
// 方法1:使用 innerHTML
container.innerHTML = '';
// 方法2:逐个移除子元素
while (container.firstChild) {
container.removeChild(container.firstChild);
}
// 方法3:使用 removeChild 和 lastChild
while (container.lastChild) {
container.removeChild(container.lastChild);
}
4. 条件移除元素
// 根据条件移除元素
function removeElementsByCondition(parent, conditionFn) {
const children = Array.from(parent.children);
children.forEach(child => {
if (conditionFn(child)) {
child.remove();
}
});
}
// 示例:移除所有空的 div 元素
removeElementsByCondition(document.body, (element) => {
return element.tagName === 'DIV' && element.textContent.trim() === '';
});
5. 移除事件监听器
// 移除元素前清理事件监听器(防止内存泄漏)
const button = document.getElementById('myButton');
const handleClick = function() {
console.log('Button clicked');
};
button.addEventListener('click', handleClick);
// 移除元素时
button.removeEventListener('click', handleClick);
button.remove();
克隆 DOM 元素
1. 使用 cloneNode 方法
// 浅克隆:只克隆元素本身,不包括子元素
const originalElement = document.getElementById('original');
const shallowClone = originalElement.cloneNode(false);
// 深克隆:克隆元素及其所有子元素和文本内容
const deepClone = originalElement.cloneNode(true);
// 插入克隆的元素
document.getElementById('container').appendChild(deepClone);
2. 克隆元素的属性处理
const original = document.getElementById('original');
const clone = original.cloneNode(true);
// 注意:克隆会复制所有属性,包括 id
console.log(clone.id); // 与 original 相同的 id
// 修改克隆元素的 id 以避免重复
clone.id = 'clone-' + original.id;
// 克隆会保留事件监听器吗?不会!
// 需要手动重新添加事件监听器
clone.addEventListener('click', function() {
console.log('Clone clicked');
});
3. 深度克隆与浅度克隆的区别
<div id="parent">
<span>Child 1</span>
<p>Child 2</p>
</div>
const parent = document.getElementById('parent');
// 浅克隆
const shallow = parent.cloneNode(false);
console.log(shallow.children.length); // 0 - 没有子元素
// 深克隆
const deep = parent.cloneNode(true);
console.log(deep.children.length); // 2 - 包含所有子元素
console.log(deep.children[0].textContent); // "Child 1"
console.log(deep.children[1].textContent); // "Child 2"
4. 克隆并修改元素
function cloneAndModify(originalId, modifications) {
const original = document.getElementById(originalId);
const clone = original.cloneNode(true);
// 修改属性
if (modifications.id) {
clone.id = modifications.id;
}
if (modifications.className) {
clone.className = modifications.className;
}
// 修改内容
if (modifications.textContent) {
clone.textContent = modifications.textContent;
}
// 修改样式
if (modifications.style) {
Object.assign(clone.style, modifications.style);
}
return clone;
}
// 使用示例
const newElement = cloneAndModify('template', {
id: 'new-item',
className: 'item cloned-item',
textContent: 'Cloned Item',
style: {
color: 'blue',
fontWeight: 'bold'
}
});
document.getElementById('container').appendChild(newElement);
5. 批量克隆元素
function cloneMultipleElements(selector, count, containerId) {
const original = document.querySelector(selector);
const container = document.getElementById(containerId);
for (let i = 0; i < count; i++) {
const clone = original.cloneNode(true);
clone.id = `${original.id}-${i}`; // 确保唯一 id
clone.setAttribute('data-index', i);
container.appendChild(clone);
}
}
// 使用示例
cloneMultipleElements('.template-item', 5, 'item-container');
6. 克隆时处理特殊属性
function smartClone(element) {
const clone = element.cloneNode(true);
// 处理表单元素的值
const originalInputs = element.querySelectorAll('input, textarea, select');
const cloneInputs = clone.querySelectorAll('input, textarea, select');
originalInputs.forEach((input, index) => {
const cloneInput = cloneInputs[index];
if (input.type === 'checkbox' || input.type === 'radio') {
cloneInput.checked = input.checked;
} else {
cloneInput.value = input.value;
}
});
// 处理自定义属性
const dataAttributes = element.dataset;
Object.keys(dataAttributes).forEach(key => {
clone.dataset[key] = dataAttributes[key];
});
return clone;
}
性能监控和调试
1. 使用 Performance API 监控 DOM 操作性能
// 监控 DOM 操作性能
function measureDOMOperation(operation, label) {
performance.mark(`${label}-start`);
operation();
performance.mark(`${label}-end`);
performance.measure(label, `${label}-start`, `${label}-end`);
const measure = performance.getEntriesByName(label)[0];
console.log(`${label} took ${measure.duration} milliseconds`);
}
// 使用示例
measureDOMOperation(() => {
// 执行 DOM 操作
const container = document.getElementById('container');
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
div.textContent = `Item ${i}`;
container.appendChild(div);
}
}, 'DOM Insertion');
2. 使用浏览器开发者工具
- 使用 Chrome DevTools 的 Performance 面板分析重排和重绘
- 使用 Memory 面板检查内存泄漏
- 使用 Rendering 工具可视化重排和重绘
这些全面的 DOM 操作策略可以帮助显著提高 Web 应用的性能,特别是在处理大量元素或频繁更新的场景中。
更多推荐


所有评论(0)