# 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操作是前端开发的基础,也是实现复杂用户界面的关键。