JavaScript DOM操作详解

# JavaScript DOM操作详解

## 1. DOM简介

### 1.1 什么是DOM

DOM(Document Object Model)是文档对象模型,是一种用于HTML和XML文档的编程接口。它将文档表示为一个树形结构,其中每个节点都是文档的一个部分。

### 1.2 DOM的结构

DOM树由以下类型的节点组成:
– **文档节点**:整个文档的根节点
– **元素节点**:HTML标签
– **文本节点**:元素内的文本
– **属性节点**:元素的属性
– **注释节点**:注释内容

### 1.3 DOM的作用

– 访问和修改HTML元素
– 动态创建和删除元素
– 改变元素的样式和内容
– 响应用户事件
– 操作表单元素

## 2. 选择元素

### 2.1 getElementById

通过元素的ID选择元素,返回单个元素。

“`javascript
const element = document.getElementById(‘myElement’);
console.log(element);
“`

### 2.2 getElementsByClassName

通过元素的类名选择元素,返回一个HTMLCollection。

“`javascript
const elements = document.getElementsByClassName(‘myClass’);
console.log(elements[0]); // 访问第一个元素

// 遍历所有元素
for (let i = 0; i < elements.length; i++) { console.log(elements[i]); } ``` ### 2.3 getElementsByTagName 通过元素的标签名选择元素,返回一个HTMLCollection。 ```javascript const elements = document.getElementsByTagName('div'); console.log(elements[0]); // 访问第一个元素 // 遍历所有元素 for (let i = 0; i < elements.length; i++) { console.log(elements[i]); } ``` ### 2.4 querySelector 通过CSS选择器选择第一个匹配的元素,返回单个元素。 ```javascript const element = document.querySelector('.myClass'); console.log(element); // 复杂选择器 const element = document.querySelector('div > p.first’);
console.log(element);
“`

### 2.5 querySelectorAll

通过CSS选择器选择所有匹配的元素,返回一个NodeList。

“`javascript
const elements = document.querySelectorAll(‘.myClass’);
console.log(elements[0]); // 访问第一个元素

// 遍历所有元素
elements.forEach(element => {
console.log(element);
});

// 也可以使用for循环
for (let i = 0; i < elements.length; i++) { console.log(elements[i]); } ``` ## 3. 修改元素 ### 3.1 修改内容 #### 3.1.1 innerHTML 设置或获取元素的HTML内容。 ```javascript // 获取内容 const content = element.innerHTML; console.log(content); // 设置内容 element.innerHTML = '

New content

‘;
“`

#### 3.1.2 textContent

设置或获取元素的文本内容,不包括HTML标签。

“`javascript
// 获取内容
const text = element.textContent;
console.log(text);

// 设置内容
element.textContent = ‘New text content’;
“`

#### 3.1.3 innerText

设置或获取元素的可见文本内容。

“`javascript
// 获取内容
const text = element.innerText;
console.log(text);

// 设置内容
element.innerText = ‘New visible text’;
“`

### 3.2 修改属性

#### 3.2.1 setAttribute

设置元素的属性。

“`javascript
element.setAttribute(‘class’, ‘newClass’);
element.setAttribute(‘data-id’, ‘123’);
“`

#### 3.2.2 getAttribute

获取元素的属性值。

“`javascript
const className = element.getAttribute(‘class’);
const dataId = element.getAttribute(‘data-id’);
console.log(className, dataId);
“`

#### 3.2.3 removeAttribute

移除元素的属性。

“`javascript
element.removeAttribute(‘class’);
element.removeAttribute(‘data-id’);
“`

#### 3.2.4 直接修改属性

对于某些标准属性,可以直接修改。

“`javascript
element.id = ‘newId’;
element.className = ‘newClass’;
element.src = ‘new-image.jpg’;
element.href = ‘https://example.com’;
“`

### 3.3 修改样式

#### 3.3.1 style属性

直接修改元素的style属性。

“`javascript
element.style.color = ‘red’;
element.style.fontSize = ’16px’;
element.style.backgroundColor = ‘#f0f0f0′;
element.style.marginTop = ’10px’;
“`

#### 3.3.2 classList

使用classList操作元素的类。

“`javascript
// 添加类
element.classList.add(‘newClass’);

// 移除类
element.classList.remove(‘oldClass’);

// 切换类
element.classList.toggle(‘active’);

// 检查类是否存在
const hasClass = element.classList.contains(‘active’);
console.log(hasClass);
“`

## 4. 创建和删除元素

### 4.1 创建元素

#### 4.1.1 createElement

创建一个新的元素节点。

“`javascript
const newElement = document.createElement(‘div’);
newElement.textContent = ‘New element’;
newElement.className = ‘newClass’;
“`

#### 4.1.2 createTextNode

创建一个新的文本节点。

“`javascript
const textNode = document.createTextNode(‘Text content’);
“`

#### 4.1.3 createDocumentFragment

创建一个文档片段,用于批量添加元素。

“`javascript
const fragment = document.createDocumentFragment();

// 添加多个元素到片段
for (let i = 0; i < 10; i++) { const div = document.createElement('div'); div.textContent = `Element ${i}`; fragment.appendChild(div); } // 一次性添加到DOM document.body.appendChild(fragment); ``` ### 4.2 添加元素 #### 4.2.1 appendChild 将元素添加到父元素的末尾。 ```javascript const parent = document.getElementById('parent'); const child = document.createElement('div'); child.textContent = 'Child element'; parent.appendChild(child); ``` #### 4.2.2 insertBefore 在指定元素之前插入元素。 ```javascript const parent = document.getElementById('parent'); const newElement = document.createElement('div'); newElement.textContent = 'New element'; const referenceElement = document.getElementById('reference'); parent.insertBefore(newElement, referenceElement); ``` #### 4.2.3 prepend 将元素添加到父元素的开头。 ```javascript const parent = document.getElementById('parent'); const newElement = document.createElement('div'); newElement.textContent = 'First element'; parent.prepend(newElement); ``` #### 4.2.4 append 将多个元素添加到父元素的末尾。 ```javascript const parent = document.getElementById('parent'); const element1 = document.createElement('div'); element1.textContent = 'Element 1'; const element2 = document.createElement('div'); element2.textContent = 'Element 2'; parent.append(element1, element2); ``` ### 4.3 删除元素 #### 4.3.1 remove 移除元素自身。 ```javascript const element = document.getElementById('element'); element.remove(); ``` #### 4.3.2 removeChild 从父元素中移除子元素。 ```javascript const parent = document.getElementById('parent'); const child = document.getElementById('child'); parent.removeChild(child); ``` ## 5. 遍历DOM ### 5.1 父节点 ```javascript const child = document.getElementById('child'); const parent = child.parentNode; console.log(parent); // 也可以使用 parentElement const parentElement = child.parentElement; console.log(parentElement); ``` ### 5.2 子节点 ```javascript const parent = document.getElementById('parent'); // 获取所有子节点(包括文本节点) const childNodes = parent.childNodes; console.log(childNodes); // 获取所有子元素 const children = parent.children; console.log(children); // 获取第一个子节点 const firstChild = parent.firstChild; console.log(firstChild); // 获取第一个子元素 const firstElementChild = parent.firstElementChild; console.log(firstElementChild); // 获取最后一个子节点 const lastChild = parent.lastChild; console.log(lastChild); // 获取最后一个子元素 const lastElementChild = parent.lastElementChild; console.log(lastElementChild); ``` ### 5.3 兄弟节点 ```javascript const element = document.getElementById('element'); // 获取前一个兄弟节点 const previousSibling = element.previousSibling; console.log(previousSibling); // 获取前一个兄弟元素 const previousElementSibling = element.previousElementSibling; console.log(previousElementSibling); // 获取后一个兄弟节点 const nextSibling = element.nextSibling; console.log(nextSibling); // 获取后一个兄弟元素 const nextElementSibling = element.nextElementSibling; console.log(nextElementSibling); ``` ## 6. 事件处理 ### 6.1 添加事件监听器 ```javascript const button = document.getElementById('button'); // 基本用法 button.addEventListener('click', function() { console.log('Button clicked'); }); // 使用箭头函数 button.addEventListener('click', () => {
console.log(‘Button clicked’);
});

// 带参数的事件处理函数
button.addEventListener(‘click’, function(e) {
console.log(‘Button clicked’, e);
});
“`

### 6.2 事件类型

常见的事件类型包括:
– **click**:点击事件
– **dblclick**:双击事件
– **mousedown**:鼠标按下事件
– **mouseup**:鼠标释放事件
– **mousemove**:鼠标移动事件
– **mouseenter**:鼠标进入元素事件
– **mouseleave**:鼠标离开元素事件
– **keydown**:键盘按下事件
– **keyup**:键盘释放事件
– **input**:输入事件
– **change**:值改变事件
– **submit**:表单提交事件
– **focus**:获得焦点事件
– **blur**:失去焦点事件
– **load**:加载完成事件
– **resize**:窗口大小改变事件
– **scroll**:滚动事件

### 6.3 事件对象

事件处理函数会接收一个事件对象,包含事件的相关信息。

“`javascript
element.addEventListener(‘click’, function(e) {
// 事件类型
console.log(e.type);

// 目标元素
console.log(e.target);

// 当前元素
console.log(e.currentTarget);

// 鼠标坐标
console.log(e.clientX, e.clientY);
console.log(e.pageX, e.pageY);

// 键盘按键
console.log(e.key);
console.log(e.code);

// 阻止默认行为
e.preventDefault();

// 阻止事件传播
e.stopPropagation();
});
“`

### 6.4 事件委托

利用事件冒泡,将事件监听器绑定到父元素,处理子元素的事件。

“`javascript
const list = document.getElementById(‘list’);

list.addEventListener(‘click’, function(e) {
if (e.target.tagName === ‘LI’) {
console.log(‘List item clicked:’, e.target.textContent);
}
});
“`

### 6.5 移除事件监听器

“`javascript
function handleClick() {
console.log(‘Button clicked’);
}

const button = document.getElementById(‘button’);
button.addEventListener(‘click’, handleClick);

// 移除事件监听器
button.removeEventListener(‘click’, handleClick);
“`

## 7. 表单操作

### 7.1 获取表单元素

“`javascript
const form = document.getElementById(‘form’);
const input = document.getElementById(‘input’);
const select = document.getElementById(‘select’);
const checkbox = document.getElementById(‘checkbox’);
const radio = document.getElementById(‘radio’);
“`

### 7.2 获取和设置表单值

“`javascript
// 获取值
const inputValue = input.value;
const selectValue = select.value;
const checkboxChecked = checkbox.checked;
const radioChecked = radio.checked;

// 设置值
input.value = ‘New value’;
select.value = ‘option2’;
checkbox.checked = true;
radio.checked = true;
“`

### 7.3 表单提交

“`javascript
const form = document.getElementById(‘form’);

form.addEventListener(‘submit’, function(e) {
e.preventDefault(); // 阻止默认提交

// 处理表单数据
const formData = new FormData(form);

// 遍历表单数据
for (const [name, value] of formData.entries()) {
console.log(name, value);
}

// 或者获取特定字段
const username = formData.get(‘username’);
const password = formData.get(‘password’);

// 发送表单数据
fetch(‘/api/submit’, {
method: ‘POST’,
body: formData
})
.then(response => response.json())
.then(data => {
console.log(‘Success:’, data);
})
.catch(error => {
console.error(‘Error:’, error);
});
});
“`

## 8. 样式操作

### 8.1 获取计算样式

“`javascript
const element = document.getElementById(‘element’);
const computedStyle = window.getComputedStyle(element);

const color = computedStyle.color;
const fontSize = computedStyle.fontSize;
const backgroundColor = computedStyle.backgroundColor;

console.log(color, fontSize, backgroundColor);
“`

### 8.2 操作CSS类

“`javascript
const element = document.getElementById(‘element’);

// 添加类
element.classList.add(‘class1’, ‘class2’);

// 移除类
element.classList.remove(‘class1’);

// 切换类
element.classList.toggle(‘active’);

// 检查类是否存在
const hasClass = element.classList.contains(‘active’);
console.log(hasClass);

// 替换类
element.classList.replace(‘oldClass’, ‘newClass’);
“`

### 8.3 动画效果

“`javascript
const element = document.getElementById(‘element’);

// 添加动画类
element.classList.add(‘animate’);

// 监听动画结束事件
element.addEventListener(‘animationend’, function() {
console.log(‘Animation ended’);
element.classList.remove(‘animate’);
});

// 或者使用requestAnimationFrame
function animate(element, targetX) {
let currentX = 0;
const startTime = performance.now();

function update(currentTime) {
const elapsed = currentTime – startTime;
const progress = Math.min(elapsed / 1000, 1); // 1秒动画
currentX = progress * targetX;

element.style.transform = `translateX(${currentX}px)`;

if (progress < 1) { requestAnimationFrame(update); } } requestAnimationFrame(update); } // 使用 animate(element, 200); ``` ## 9. 高级DOM操作 ### 9.1 克隆元素 ```javascript const element = document.getElementById('element'); // 浅克隆(不克隆子元素) const shallowClone = element.cloneNode(false); // 深克隆(克隆子元素) const deepClone = element.cloneNode(true); // 添加到DOM document.body.appendChild(deepClone); ``` ### 9.2 操作HTML属性 ```javascript const element = document.getElementById('element'); // 检查属性是否存在 const hasAttribute = element.hasAttribute('data-id'); console.log(hasAttribute); // 设置属性 element.setAttribute('data-id', '123'); // 获取属性 const dataId = element.getAttribute('data-id'); console.log(dataId); // 移除属性 element.removeAttribute('data-id'); ``` ### 9.3 操作数据集 使用dataset操作data-*属性。 ```html

“`

“`javascript
const element = document.getElementById(‘element’);

// 获取data属性
const id = element.dataset.id;
const name = element.dataset.name;
console.log(id, name); // 123 John

// 设置data属性
element.dataset.age = ’30’;
element.dataset.city = ‘New York’;

// 移除data属性
delete element.dataset.name;
“`

### 9.4 滚动操作

“`javascript
// 滚动到顶部
window.scrollTo(0, 0);

// 滚动到指定位置
window.scrollTo({ top: 100, left: 0, behavior: ‘smooth’ });

// 滚动到元素
const element = document.getElementById(‘element’);
element.scrollIntoView({ behavior: ‘smooth’ });

// 获取滚动位置
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
console.log(scrollTop, scrollLeft);

// 监听滚动事件
window.addEventListener(‘scroll’, function() {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
console.log(‘Scroll position:’, scrollTop);
});
“`

### 9.5 尺寸和位置

“`javascript
const element = document.getElementById(‘element’);

// 获取元素尺寸
const rect = element.getBoundingClientRect();
console.log(rect.width, rect.height);
console.log(rect.top, rect.left, rect.bottom, rect.right);

// 获取元素偏移
const offsetTop = element.offsetTop;
const offsetLeft = element.offsetLeft;
console.log(offsetTop, offsetLeft);

// 获取元素父容器
const offsetParent = element.offsetParent;
console.log(offsetParent);

// 获取元素的client尺寸
const clientWidth = element.clientWidth;
const clientHeight = element.clientHeight;
console.log(clientWidth, clientHeight);

// 获取元素的scroll尺寸
const scrollWidth = element.scrollWidth;
const scrollHeight = element.scrollHeight;
console.log(scrollWidth, scrollHeight);

// 获取元素的scroll位置
const scrollTop = element.scrollTop;
const scrollLeft = element.scrollLeft;
console.log(scrollTop, scrollLeft);
“`

## 10. DOM操作的性能优化

### 10.1 减少DOM操作

– **批量更新**:使用文档片段或字符串拼接
– **减少重排**:避免频繁修改布局属性
– **减少重绘**:避免频繁修改颜色、背景等属性
– **使用CSS类**:通过添加/移除类来修改样式

“`javascript
// 优化前
for (let i = 0; i < 1000; i++) { const div = document.createElement('div'); div.textContent = `Item ${i}`; document.body.appendChild(div); } // 优化后 const fragment = document.createDocumentFragment(); for (let i = 0; i < 1000; i++) { const div = document.createElement('div'); div.textContent = `Item ${i}`; fragment.appendChild(div); } document.body.appendChild(fragment); ``` ### 10.2 缓存DOM查询 - **缓存频繁访问的元素**:避免重复查询DOM - **缩小查询范围**:从特定元素开始查询 ```javascript // 优化前 function updateItems() { document.getElementById('list').innerHTML = ''; const items = document.querySelectorAll('.item'); // 操作 } // 优化后 const list = document.getElementById('list'); function updateItems() { list.innerHTML = ''; const items = list.querySelectorAll('.item'); // 操作 } ``` ### 10.3 使用事件委托 - **减少事件监听器数量**:将事件监听器绑定到父元素 - **处理动态元素**:新添加的元素自动继承事件处理 ```javascript // 优化前 const items = document.querySelectorAll('li'); items.forEach(item => {
item.addEventListener(‘click’, function() {
console.log(‘Item clicked:’, this.textContent);
});
});

// 优化后
const list = document.querySelector(‘ul’);
list.addEventListener(‘click’, function(e) {
if (e.target.tagName === ‘LI’) {
console.log(‘Item clicked:’, e.target.textContent);
}
});
“`

### 10.4 避免强制同步布局

– **避免在修改样式后立即读取布局属性**:这会触发强制同步布局

“`javascript
// 优化前
function updateElements(elements) {
elements.forEach(element => {
element.style.width = `${element.offsetWidth * 2}px`; // 读取后立即修改,导致强制同步布局
});
}

// 优化后
function updateElements(elements) {
// 先读取所有宽度
const widths = elements.map(element => element.offsetWidth);

// 再一次性修改
elements.forEach((element, index) => {
element.style.width = `${widths[index] * 2}px`;
});
}
“`

## 11. 实用工具函数

### 11.1 元素操作

“`javascript
// 检查元素是否在视口中
function isInViewport(element) {
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); } // 平滑滚动到元素 function scrollToElement(element, offset = 0) { const elementPosition = element.getBoundingClientRect().top; const offsetPosition = elementPosition + window.pageYOffset - offset; window.scrollTo({ top: offsetPosition, behavior: 'smooth' }); } // 复制文本到剪贴板 function copyToClipboard(text) { return navigator.clipboard.writeText(text) .then(() => true)
.catch(err => {
console.error(‘Failed to copy: ‘, err);
return false;
});
}
“`

### 11.2 事件处理

“`javascript
// 防抖
function debounce(func, wait) {
let timeout;
return function() {
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, arguments);
}, wait);
};
}

// 节流
function throttle(func, limit) {
let inThrottle;
return function() {
if (!inThrottle) {
func.apply(this, arguments);
inThrottle = true;
setTimeout(() => {
inThrottle = false;
}, limit);
}
};
}

// 事件委托
function delegateEvent(parent, selector, eventType, handler) {
parent.addEventListener(eventType, function(e) {
const target = e.target.closest(selector);
if (target) {
handler.call(target, e);
}
});
}
“`

## 12. 总结

– **DOM操作**是JavaScript前端开发的核心,用于与HTML文档交互
– **选择元素**:使用getElementById、querySelector等方法
– **修改元素**:修改内容、属性和样式
– **创建和删除元素**:动态操作DOM结构
– **遍历DOM**:导航DOM树的节点
– **事件处理**:响应用户交互
– **表单操作**:处理用户输入
– **样式操作**:修改元素样式和动画
– **高级操作**:克隆元素、操作属性、滚动等
– **性能优化**:减少DOM操作、缓存查询、使用事件委托等

通过掌握DOM操作的各种技术和最佳实践,我们可以创建更加交互性强、响应迅速的Web应用。DOM操作是前端开发的基础,也是实现复杂用户界面的关键。